summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp11
-rw-r--r--OWNERS2
-rw-r--r--PREUPLOAD.cfg8
-rw-r--r--PermissionController/Android.bp97
-rw-r--r--PermissionController/AndroidManifest-lib.xml18
-rw-r--r--PermissionController/AndroidManifest.xml50
-rw-r--r--PermissionController/OWNERS13
-rw-r--r--PermissionController/TELEVISION_OWNERS2
-rw-r--r--PermissionController/TEST_MAPPING148
-rw-r--r--PermissionController/WEAR_OWNERS3
-rw-r--r--PermissionController/iconloaderlib/Android.bp1
-rw-r--r--PermissionController/jarjar-rules.txt12
-rw-r--r--PermissionController/lint-baseline.xml697
-rw-r--r--PermissionController/proguard.flags10
-rw-r--r--PermissionController/res/drawable/ic_edit.xml24
-rw-r--r--PermissionController/res/drawable/ic_lock_closed.xml28
-rw-r--r--PermissionController/res/drawable/ic_more_horizontal.xml26
-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.xml103
-rw-r--r--PermissionController/res/layout/car_warning_banner_preference_card.xml47
-rw-r--r--PermissionController/res/layout/enhanced_confirmation_dialog.xml44
-rw-r--r--PermissionController/res/layout/grant_permissions.xml5
-rw-r--r--PermissionController/res/layout/grant_permissions_material3.xml3
-rw-r--r--PermissionController/res/layout/request_role_item.xml5
-rw-r--r--PermissionController/res/navigation-watch/nav_graph.xml126
-rw-r--r--PermissionController/res/values-af-v34/strings.xml1
-rw-r--r--PermissionController/res/values-af-watch/strings.xml7
-rw-r--r--PermissionController/res/values-af/strings.xml158
-rw-r--r--PermissionController/res/values-am-v34/strings.xml1
-rw-r--r--PermissionController/res/values-am-watch/strings.xml7
-rw-r--r--PermissionController/res/values-am/strings.xml74
-rw-r--r--PermissionController/res/values-ar-v34/strings.xml3
-rw-r--r--PermissionController/res/values-ar-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ar/strings.xml116
-rw-r--r--PermissionController/res/values-as-v34/strings.xml1
-rw-r--r--PermissionController/res/values-as-watch/strings.xml7
-rw-r--r--PermissionController/res/values-as/strings.xml74
-rw-r--r--PermissionController/res/values-az-v34/strings.xml1
-rw-r--r--PermissionController/res/values-az-watch/strings.xml7
-rw-r--r--PermissionController/res/values-az/strings.xml74
-rw-r--r--PermissionController/res/values-b+sr+Latn-v34/strings.xml1
-rw-r--r--PermissionController/res/values-b+sr+Latn-watch/strings.xml7
-rw-r--r--PermissionController/res/values-b+sr+Latn/strings.xml96
-rw-r--r--PermissionController/res/values-be-v34/strings.xml1
-rw-r--r--PermissionController/res/values-be-watch/strings.xml7
-rw-r--r--PermissionController/res/values-be/strings.xml72
-rw-r--r--PermissionController/res/values-bg-v34/strings.xml1
-rw-r--r--PermissionController/res/values-bg-watch/strings.xml7
-rw-r--r--PermissionController/res/values-bg/strings.xml74
-rw-r--r--PermissionController/res/values-bn-v34/strings.xml1
-rw-r--r--PermissionController/res/values-bn-watch/strings.xml7
-rw-r--r--PermissionController/res/values-bn/strings.xml76
-rw-r--r--PermissionController/res/values-bs-v34/strings.xml1
-rw-r--r--PermissionController/res/values-bs-watch/strings.xml7
-rw-r--r--PermissionController/res/values-bs/strings.xml80
-rw-r--r--PermissionController/res/values-ca-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ca-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ca/strings.xml82
-rw-r--r--PermissionController/res/values-cs-v33/strings.xml2
-rw-r--r--PermissionController/res/values-cs-v34/strings.xml1
-rw-r--r--PermissionController/res/values-cs-watch/strings.xml7
-rw-r--r--PermissionController/res/values-cs/strings.xml80
-rw-r--r--PermissionController/res/values-da-v34/strings.xml1
-rw-r--r--PermissionController/res/values-da-watch/strings.xml7
-rw-r--r--PermissionController/res/values-da/strings.xml106
-rw-r--r--PermissionController/res/values-de-v34/strings.xml3
-rw-r--r--PermissionController/res/values-de-watch/strings.xml7
-rw-r--r--PermissionController/res/values-de/strings.xml90
-rw-r--r--PermissionController/res/values-el-car/strings.xml2
-rw-r--r--PermissionController/res/values-el-v34/strings.xml1
-rw-r--r--PermissionController/res/values-el-watch/strings.xml7
-rw-r--r--PermissionController/res/values-el/strings.xml86
-rw-r--r--PermissionController/res/values-en-rAU-v34/strings.xml1
-rw-r--r--PermissionController/res/values-en-rAU-watch/strings.xml7
-rw-r--r--PermissionController/res/values-en-rAU/strings.xml74
-rw-r--r--PermissionController/res/values-en-rCA-v34/strings.xml1
-rw-r--r--PermissionController/res/values-en-rCA-watch/strings.xml7
-rw-r--r--PermissionController/res/values-en-rCA/strings.xml70
-rw-r--r--PermissionController/res/values-en-rGB-v34/strings.xml1
-rw-r--r--PermissionController/res/values-en-rGB-watch/strings.xml7
-rw-r--r--PermissionController/res/values-en-rGB/strings.xml74
-rw-r--r--PermissionController/res/values-en-rIN-v34/strings.xml1
-rw-r--r--PermissionController/res/values-en-rIN-watch/strings.xml7
-rw-r--r--PermissionController/res/values-en-rIN/strings.xml74
-rw-r--r--PermissionController/res/values-en-rXC-v34/strings.xml1
-rw-r--r--PermissionController/res/values-en-rXC-watch/strings.xml7
-rw-r--r--PermissionController/res/values-en-rXC/strings.xml70
-rw-r--r--PermissionController/res/values-es-rUS-v34/strings.xml3
-rw-r--r--PermissionController/res/values-es-rUS-watch/strings.xml7
-rw-r--r--PermissionController/res/values-es-rUS/strings.xml94
-rw-r--r--PermissionController/res/values-es-v34/strings.xml1
-rw-r--r--PermissionController/res/values-es-watch/strings.xml7
-rw-r--r--PermissionController/res/values-es/strings.xml84
-rw-r--r--PermissionController/res/values-et-v34/strings.xml1
-rw-r--r--PermissionController/res/values-et-watch/strings.xml7
-rw-r--r--PermissionController/res/values-et/strings.xml82
-rw-r--r--PermissionController/res/values-eu-v34/strings.xml1
-rw-r--r--PermissionController/res/values-eu-watch/strings.xml7
-rw-r--r--PermissionController/res/values-eu/strings.xml86
-rw-r--r--PermissionController/res/values-fa-v34/strings.xml1
-rw-r--r--PermissionController/res/values-fa-watch/strings.xml7
-rw-r--r--PermissionController/res/values-fa/strings.xml130
-rw-r--r--PermissionController/res/values-fi-v34/strings.xml1
-rw-r--r--PermissionController/res/values-fi-watch/strings.xml7
-rw-r--r--PermissionController/res/values-fi/strings.xml80
-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.xml5
-rw-r--r--PermissionController/res/values-fr-rCA-watch/strings.xml9
-rw-r--r--PermissionController/res/values-fr-rCA/strings.xml446
-rw-r--r--PermissionController/res/values-fr-television/strings.xml2
-rw-r--r--PermissionController/res/values-fr-v34/strings.xml1
-rw-r--r--PermissionController/res/values-fr-watch/strings.xml7
-rw-r--r--PermissionController/res/values-fr/strings.xml86
-rw-r--r--PermissionController/res/values-gl-v34/strings.xml1
-rw-r--r--PermissionController/res/values-gl-watch/strings.xml7
-rw-r--r--PermissionController/res/values-gl/strings.xml88
-rw-r--r--PermissionController/res/values-gu-v34/strings.xml1
-rw-r--r--PermissionController/res/values-gu-watch/strings.xml7
-rw-r--r--PermissionController/res/values-gu/strings.xml82
-rw-r--r--PermissionController/res/values-hi-v34/strings.xml3
-rw-r--r--PermissionController/res/values-hi-watch/strings.xml7
-rw-r--r--PermissionController/res/values-hi/strings.xml114
-rw-r--r--PermissionController/res/values-hr-v33/strings.xml2
-rw-r--r--PermissionController/res/values-hr-v34/strings.xml1
-rw-r--r--PermissionController/res/values-hr-watch/strings.xml7
-rw-r--r--PermissionController/res/values-hr/strings.xml96
-rw-r--r--PermissionController/res/values-hu-v34/strings.xml1
-rw-r--r--PermissionController/res/values-hu-watch/strings.xml7
-rw-r--r--PermissionController/res/values-hu/strings.xml70
-rw-r--r--PermissionController/res/values-hy-v34/strings.xml3
-rw-r--r--PermissionController/res/values-hy-watch/strings.xml7
-rw-r--r--PermissionController/res/values-hy/strings.xml104
-rw-r--r--PermissionController/res/values-in-v33/strings.xml2
-rw-r--r--PermissionController/res/values-in-v34/strings.xml1
-rw-r--r--PermissionController/res/values-in-watch/strings.xml7
-rw-r--r--PermissionController/res/values-in/strings.xml80
-rw-r--r--PermissionController/res/values-is-v34/strings.xml1
-rw-r--r--PermissionController/res/values-is-watch/strings.xml7
-rw-r--r--PermissionController/res/values-is/strings.xml78
-rw-r--r--PermissionController/res/values-it-v34/strings.xml1
-rw-r--r--PermissionController/res/values-it-watch/strings.xml7
-rw-r--r--PermissionController/res/values-it/strings.xml80
-rw-r--r--PermissionController/res/values-iw-v34/strings.xml1
-rw-r--r--PermissionController/res/values-iw-watch/strings.xml7
-rw-r--r--PermissionController/res/values-iw/strings.xml80
-rw-r--r--PermissionController/res/values-ja-v33/strings.xml2
-rw-r--r--PermissionController/res/values-ja-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ja-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ja/strings.xml92
-rw-r--r--PermissionController/res/values-ka-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ka-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ka/strings.xml70
-rw-r--r--PermissionController/res/values-kk-v34/strings.xml1
-rw-r--r--PermissionController/res/values-kk-watch/strings.xml7
-rw-r--r--PermissionController/res/values-kk/strings.xml84
-rw-r--r--PermissionController/res/values-km-v34/strings.xml3
-rw-r--r--PermissionController/res/values-km-watch/strings.xml7
-rw-r--r--PermissionController/res/values-km/strings.xml78
-rw-r--r--PermissionController/res/values-kn-v34/strings.xml1
-rw-r--r--PermissionController/res/values-kn-watch/strings.xml7
-rw-r--r--PermissionController/res/values-kn/strings.xml76
-rw-r--r--PermissionController/res/values-ko-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ko-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ko/strings.xml76
-rw-r--r--PermissionController/res/values-ky-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ky-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ky/strings.xml90
-rw-r--r--PermissionController/res/values-lo-v34/strings.xml1
-rw-r--r--PermissionController/res/values-lo-watch/strings.xml7
-rw-r--r--PermissionController/res/values-lo/strings.xml70
-rw-r--r--PermissionController/res/values-lt-v34/strings.xml1
-rw-r--r--PermissionController/res/values-lt-watch/strings.xml7
-rw-r--r--PermissionController/res/values-lt/strings.xml72
-rw-r--r--PermissionController/res/values-lv-v34/strings.xml1
-rw-r--r--PermissionController/res/values-lv-watch/strings.xml7
-rw-r--r--PermissionController/res/values-lv/strings.xml76
-rw-r--r--PermissionController/res/values-mk-v34/strings.xml1
-rw-r--r--PermissionController/res/values-mk-watch/strings.xml7
-rw-r--r--PermissionController/res/values-mk/strings.xml86
-rw-r--r--PermissionController/res/values-ml-v33/strings.xml2
-rw-r--r--PermissionController/res/values-ml-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ml-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ml/strings.xml74
-rw-r--r--PermissionController/res/values-mn-v34/strings.xml1
-rw-r--r--PermissionController/res/values-mn-watch/strings.xml7
-rw-r--r--PermissionController/res/values-mn/strings.xml78
-rw-r--r--PermissionController/res/values-mr-v34/strings.xml1
-rw-r--r--PermissionController/res/values-mr-watch/strings.xml7
-rw-r--r--PermissionController/res/values-mr/strings.xml78
-rw-r--r--PermissionController/res/values-ms-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ms-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ms/strings.xml74
-rw-r--r--PermissionController/res/values-my-v34/strings.xml3
-rw-r--r--PermissionController/res/values-my-watch/strings.xml7
-rw-r--r--PermissionController/res/values-my/strings.xml94
-rw-r--r--PermissionController/res/values-nb-v34/strings.xml1
-rw-r--r--PermissionController/res/values-nb-watch/strings.xml7
-rw-r--r--PermissionController/res/values-nb/strings.xml72
-rw-r--r--PermissionController/res/values-ne-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ne-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ne/strings.xml182
-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-v34/strings.xml1
-rw-r--r--PermissionController/res/values-nl-watch/strings.xml7
-rw-r--r--PermissionController/res/values-nl/strings.xml120
-rw-r--r--PermissionController/res/values-or-v34/strings.xml1
-rw-r--r--PermissionController/res/values-or-watch/strings.xml7
-rw-r--r--PermissionController/res/values-or/strings.xml86
-rw-r--r--PermissionController/res/values-pa-v34/strings.xml3
-rw-r--r--PermissionController/res/values-pa-watch/strings.xml7
-rw-r--r--PermissionController/res/values-pa/strings.xml98
-rw-r--r--PermissionController/res/values-pl-v34/strings.xml1
-rw-r--r--PermissionController/res/values-pl-watch/strings.xml7
-rw-r--r--PermissionController/res/values-pl/strings.xml82
-rw-r--r--PermissionController/res/values-pt-rBR-v34/strings.xml3
-rw-r--r--PermissionController/res/values-pt-rBR-watch/strings.xml7
-rw-r--r--PermissionController/res/values-pt-rBR/strings.xml88
-rw-r--r--PermissionController/res/values-pt-rPT-v34/strings.xml1
-rw-r--r--PermissionController/res/values-pt-rPT-watch/strings.xml7
-rw-r--r--PermissionController/res/values-pt-rPT/strings.xml102
-rw-r--r--PermissionController/res/values-pt-v34/strings.xml3
-rw-r--r--PermissionController/res/values-pt-watch/strings.xml7
-rw-r--r--PermissionController/res/values-pt/strings.xml88
-rw-r--r--PermissionController/res/values-ro-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ro-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ro/strings.xml78
-rw-r--r--PermissionController/res/values-ru-v34/strings.xml3
-rw-r--r--PermissionController/res/values-ru-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ru/strings.xml92
-rw-r--r--PermissionController/res/values-si-v34/strings.xml1
-rw-r--r--PermissionController/res/values-si-watch/strings.xml7
-rw-r--r--PermissionController/res/values-si/strings.xml74
-rw-r--r--PermissionController/res/values-sk-v34/strings.xml1
-rw-r--r--PermissionController/res/values-sk-watch/strings.xml7
-rw-r--r--PermissionController/res/values-sk/strings.xml100
-rw-r--r--PermissionController/res/values-sl-v34/strings.xml1
-rw-r--r--PermissionController/res/values-sl-watch/strings.xml7
-rw-r--r--PermissionController/res/values-sl/strings.xml76
-rw-r--r--PermissionController/res/values-sq-v34/strings.xml1
-rw-r--r--PermissionController/res/values-sq-watch/strings.xml7
-rw-r--r--PermissionController/res/values-sq/strings.xml76
-rw-r--r--PermissionController/res/values-sr-v34/strings.xml1
-rw-r--r--PermissionController/res/values-sr-watch/strings.xml7
-rw-r--r--PermissionController/res/values-sr/strings.xml96
-rw-r--r--PermissionController/res/values-sv-v34/strings.xml1
-rw-r--r--PermissionController/res/values-sv-watch/strings.xml7
-rw-r--r--PermissionController/res/values-sv/strings.xml74
-rw-r--r--PermissionController/res/values-sw-v34/strings.xml3
-rw-r--r--PermissionController/res/values-sw-watch/strings.xml7
-rw-r--r--PermissionController/res/values-sw/strings.xml74
-rw-r--r--PermissionController/res/values-ta-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ta-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ta/strings.xml70
-rw-r--r--PermissionController/res/values-te-v34/strings.xml1
-rw-r--r--PermissionController/res/values-te-watch/strings.xml7
-rw-r--r--PermissionController/res/values-te/strings.xml82
-rw-r--r--PermissionController/res/values-th-v34/strings.xml5
-rw-r--r--PermissionController/res/values-th-watch/strings.xml7
-rw-r--r--PermissionController/res/values-th/strings.xml80
-rw-r--r--PermissionController/res/values-tl-v34/strings.xml1
-rw-r--r--PermissionController/res/values-tl-watch/strings.xml7
-rw-r--r--PermissionController/res/values-tl/strings.xml70
-rw-r--r--PermissionController/res/values-tr-v34/strings.xml1
-rw-r--r--PermissionController/res/values-tr-watch/strings.xml7
-rw-r--r--PermissionController/res/values-tr/strings.xml74
-rw-r--r--PermissionController/res/values-uk-v34/strings.xml1
-rw-r--r--PermissionController/res/values-uk-watch/strings.xml7
-rw-r--r--PermissionController/res/values-uk/strings.xml96
-rw-r--r--PermissionController/res/values-ur-v34/strings.xml1
-rw-r--r--PermissionController/res/values-ur-watch/strings.xml7
-rw-r--r--PermissionController/res/values-ur/strings.xml72
-rw-r--r--PermissionController/res/values-uz-v34/strings.xml1
-rw-r--r--PermissionController/res/values-uz-watch/strings.xml7
-rw-r--r--PermissionController/res/values-uz/strings.xml78
-rw-r--r--PermissionController/res/values-v31/styles.xml20
-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.xml3
-rw-r--r--PermissionController/res/values-v34/styles.xml2
-rw-r--r--PermissionController/res/values-v35/bools.xml21
-rw-r--r--PermissionController/res/values-v35/themes.xml34
-rw-r--r--PermissionController/res/values-vi-v33/strings.xml2
-rw-r--r--PermissionController/res/values-vi-v34/strings.xml1
-rw-r--r--PermissionController/res/values-vi-watch/strings.xml7
-rw-r--r--PermissionController/res/values-vi/strings.xml74
-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-watch/donottranslate.xml31
-rw-r--r--PermissionController/res/values-watch/strings.xml21
-rw-r--r--PermissionController/res/values-watch/themes.xml6
-rw-r--r--PermissionController/res/values-zh-rCN-v34/strings.xml1
-rw-r--r--PermissionController/res/values-zh-rCN-watch/strings.xml7
-rw-r--r--PermissionController/res/values-zh-rCN/strings.xml96
-rw-r--r--PermissionController/res/values-zh-rHK-v34/strings.xml3
-rw-r--r--PermissionController/res/values-zh-rHK-watch/strings.xml7
-rw-r--r--PermissionController/res/values-zh-rHK/strings.xml78
-rw-r--r--PermissionController/res/values-zh-rTW-car/strings.xml2
-rw-r--r--PermissionController/res/values-zh-rTW-v34/strings.xml1
-rw-r--r--PermissionController/res/values-zh-rTW-watch/strings.xml7
-rw-r--r--PermissionController/res/values-zh-rTW/strings.xml120
-rw-r--r--PermissionController/res/values-zu-v34/strings.xml1
-rw-r--r--PermissionController/res/values-zu-watch/strings.xml7
-rw-r--r--PermissionController/res/values-zu/strings.xml76
-rw-r--r--PermissionController/res/values/bools.xml1
-rw-r--r--PermissionController/res/values/colors.xml3
-rw-r--r--PermissionController/res/values/dimens.xml1
-rw-r--r--PermissionController/res/values/overlayable.xml74
-rw-r--r--PermissionController/res/values/strings.xml212
-rw-r--r--PermissionController/res/values/styles.xml121
-rw-r--r--PermissionController/res/values/themes.xml28
-rw-r--r--PermissionController/res/xml/roles.xml160
-rw-r--r--PermissionController/res/xml/safety_center_dashboard.xml7
-rw-r--r--PermissionController/role-controller/Android.bp6
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java125
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java42
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java27
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java17
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java114
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java38
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/SystemUiRoleBehavior.java59
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v31/AutomotiveRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/AutomotiveRoleBehavior.java)2
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v31/CompanionDeviceWatchRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceWatchRoleBehavior.java)15
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v31/SystemShellRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/SystemShellRoleBehavior.java)12
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v31/TelevisionRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/TelevisionRoleBehavior.java)2
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v33/CompanionDeviceAppStreamingRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceAppStreamingRoleBehavior.java)15
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v33/CompanionDeviceComputerRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceComputerRoleBehavior.java)15
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v33/DevicePolicyManagementRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/DevicePolicyManagementRoleBehavior.java)2
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v33/DocumentManagerRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/DocumentManagerRoleBehavior.java)10
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v33/SystemWearHealthServiceRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/SystemWearHealthServiceRoleBehavior.java)2
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v34/CompanionDeviceGlassesRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceGlassesRoleBehavior.java)15
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java (renamed from PermissionController/role-controller/java/com/android/role/controller/behavior/NotesRoleBehavior.java)30
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v35/RetailDemoRoleBehavior.java65
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/behavior/v35/WalletRoleBehavior.java175
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java55
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/AppOpPermissions.java50
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Permission.java67
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Permissions.java295
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/PreferredActivity.java18
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RequiredActivity.java19
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RequiredBroadcastReceiver.java14
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RequiredComponent.java55
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RequiredContentProvider.java14
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RequiredService.java13
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java291
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java61
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java84
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/VisibilityMixin.java (renamed from PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java)28
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java (renamed from PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java)137
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/LegacyRoleFallbackEnabledUtils.java107
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/NotificationUtils.java39
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java27
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java57
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/RoleManagerCompat.java52
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java28
-rw-r--r--PermissionController/role-controller/lint-baseline.xml10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/Constants.java17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/DumpableLog.kt36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/PackageAppOpUsageModel.kt34
-rw-r--r--PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt220
-rw-r--r--PermissionController/src/com/android/permissioncontroller/auto/AutoSettingsFrameFragment.java17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt234
-rw-r--r--PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt262
-rw-r--r--PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationStatsLogUtils.kt125
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt852
-rw-r--r--PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING3
-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/incident/ConfirmationActivity.java40
-rw-r--r--PermissionController/src/com/android/permissioncontroller/incident/PendingList.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/incident/wear/ConfirmationActivityWearViewHandler.java85
-rw-r--r--PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationActivityViewModel.kt56
-rw-r--r--PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt84
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING106
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java84
-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.kt184
-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.kt93
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt166
-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.kt51
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PackagePermissionsLiveData.kt54
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt52
-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.kt71
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt75
-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.kt72
-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/repository/v31/PermissionRepository.kt136
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt149
-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.kt34
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/data/v35/PackagePermissionsExternalDeviceLiveData.kt116
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PackagePermissionGroupUsageModel.kt25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt31
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt231
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java28
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/Permission.java12
-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.kt188
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt52
-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.kt24
-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.kt20
-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.kt245
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java68
-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.java106
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionChangeStorageImpl.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java48
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt116
-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.kt657
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt83
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING59
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/v33/PermissionDecisionStorageImpl.kt62
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/Category.kt3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java686
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/LocationProviderInterceptDialog.java37
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java62
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/PermissionDialogStreamingBlockedActivity.kt44
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/RemovablePref.kt14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING19
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/UnusedAppsFragment.kt172
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java164
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoCardViewPreference.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java)22
-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.kt170
-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/GrantPermissionsAutoViewHandler.java24
-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.java128
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionGroupsFragment.java32
-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.kt352
-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/ManageStandardPermissionsFragment.java21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionAppsFragment.java60
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SmartIconLoadPackagePermissionPreference.kt17
-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/CompositeCircleView.java14
-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.java70
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageDetailsFragment.java17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java93
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageGraphicPreference.java2
-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.kt5
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/AppDataSharingUpdatesFragment.kt29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v34/PermissionRationaleViewHandlerImpl.kt26
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/legacy/PermissionUsageDetailsViewModelLegacy.kt96
-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.kt496
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionViewModel.kt1237
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt1844
-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.kt233
-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/grantPermissions/BackgroundGrantBehavior.kt210
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BasicGrantBehavior.kt58
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt82
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt56
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt126
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/NotificationGrantBehavior.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt138
-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.kt249
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt377
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/ReviewOngoingUsageViewModel.kt771
-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/AllAppPermissionsFragment.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionsFrameFragment.java2
-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/ui/viewmodel/v31/PermissionUsageViewModel.kt189
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/AppPermissionsFragmentWear.java407
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java389
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/ReviewPermissionsWearFragment.java129
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt348
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt172
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt391
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt121
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt221
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt149
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt73
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionsFragment.kt63
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt160
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionsFragment.kt78
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt174
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt235
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt158
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt97
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt161
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt74
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt160
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsFragment.kt316
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt112
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUtils.kt85
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt203
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt93
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt133
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt258
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/DrawablePainter.kt170
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Icon.kt127
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListFooter.kt77
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt126
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ResponsiveDialog.kt226
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt325
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt229
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnDefaults.kt245
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnState.kt249
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Haptics.kt292
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Rotary.kt1232
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/RotaryVelocityTracker.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionConfirmDialogViewModel.kt57
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionGroupsRevokeDialogViewModel.kt50
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt33
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt62
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt54
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearUnusedAppsViewModel.kt60
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt131
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTonalPalette.kt191
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/AndroidUtils.kt55
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/ContextCompat.java70
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/DebugUtils.kt23
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt1025
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/LocationUtils.java154
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt70
-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.java417
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/permission/utils/v35/MultiDeviceUtils.kt344
-rw-r--r--PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt36
-rw-r--r--PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt74
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySettingsUtil.kt43
-rw-r--r--PermissionController/src/com/android/permissioncontroller/privacysources/AccessibilitySourceService.kt478
-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.kt200
-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/TEST_MAPPING16
-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/Role.md4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING38
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/data/repository/v31/RoleRepository.kt77
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/CheckableLinearLayout.java10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java9
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListActivity.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java91
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java45
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java63
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java223
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleItemView.java46
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RestrictionAwarePreference.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java)10
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RestrictionAwarePreferenceMixin.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java)27
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java4
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java1
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java24
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java7
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java190
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java16
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java14
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java15
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/v35/ChangeDefaultCardEmulationActivity.java77
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppFragment.kt121
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt127
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListFragment.kt53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListHelper.kt71
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt76
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt111
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt395
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt137
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt184
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt44
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt47
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/DefaultAppConfirmDialogViewModel.kt48
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt60
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java65
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/utils/UiUtils.java26
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java81
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/SafetyCenterConstants.java3
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java29
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterJobServiceFlags.java13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt108
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java4
-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.kt16
-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.kt21
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyBrandChipPreference.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java109
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java79
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterFragment.kt30
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java8
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterSubpageFragment.kt11
-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/SafetyHomepageEntryPreference.kt6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusAnimationSequencer.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusPreference.java20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt22
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SeverityIconPicker.kt13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SnakeCaseConverter.kt2
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt71
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java20
-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/PrivacyControlsViewModel.kt18
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt49
-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.kt35
-rw-r--r--PermissionController/src/com/android/permissioncontroller/safetylabel/TEST_MAPPING41
-rw-r--r--PermissionController/src/com/android/permissioncontroller/user/data/repository/v31/UserRepository.kt80
-rw-r--r--PermissionController/tests/inprocess/Android.bp6
-rw-r--r--PermissionController/tests/inprocess/AndroidTest.xml6
-rw-r--r--PermissionController/tests/inprocess/AppThatUsesCameraPermission/Android.bp1
-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/compat/LinkMovementMethodCompatTest.java258
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/data/AttributionLabelLiveDataTest.kt7
-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.kt44
-rw-r--r--PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/util/UtilsTest.kt74
-rw-r--r--PermissionController/tests/mocking/Android.bp23
l---------PermissionController/tests/mocking/main_res1
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/AppOpRepositoryTest.kt119
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt24
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/coroutines/Flows.kt89
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationControllerTest.kt37
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/hibernation/HibernationPolicyTest.kt119
-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/repository/FakePermissionRepository.kt51
-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/domain/usecase/GetPermissionUsageUseCaseTest.kt371
-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.kt500
-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/PermissionUsageViewModelTest.kt229
-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.kt534
-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/pm/data/repository/FakePackageRepository.kt32
-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/role/data/repository/FakeRoleRepository.kt23
-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/mocking/src/com/android/permissioncontroller/tests/mocking/user/data/repository/FakeUserRepository.kt34
-rw-r--r--PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/utils/MockUtil.kt60
-rw-r--r--PermissionController/tests/outofprocess/Android.bp4
-rw-r--r--PermissionController/tests/outofprocess/src/com/android/permissioncontroller/tests/outofprocess/DumpTest.kt17
-rw-r--r--PermissionController/tests/permissionui/Android.bp4
-rw-r--r--PermissionController/tests/permissionui/AndroidTest.xml22
-rw-r--r--PermissionController/tests/permissionui/PermissionUiDefineAdditionalPermissionApp/Android.bp1
-rw-r--r--PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/Android.bp34
-rw-r--r--PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml26
-rw-r--r--PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/src/com/android/permissioncontroller/tests/appthatrequestpermission/DummyActivity.kt21
-rw-r--r--PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/Android.bp34
-rw-r--r--PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml26
-rw-r--r--PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml21
-rw-r--r--PermissionController/tests/permissionui/PermissionUiUseAdditionalPermissionApp/Android.bp1
-rw-r--r--PermissionController/tests/permissionui/PermissionUiUseCameraPermissionApp/Android.bp1
-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/PermissionUiUseStoragePermissionApp/Android.bp1
-rw-r--r--PermissionController/tests/permissionui/PermissionUiUseTwoAdditionalPermissionsApp/Android.bp1
-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.kt69
-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.kt45
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/AppPermissionFragmentTest.kt44
-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.kt24
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt148
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt103
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/LocationPermissionAppsFragmentTest.kt16
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/PermissionAppsFragmentTest.kt131
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/StoragePermissionAppsFragmentTest.kt17
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/TestAppUtils.kt20
-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.kt28
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/handheld/ManageStandardPermissionsFragmentTest.kt235
-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.kt19
-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.kt44
-rw-r--r--PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/OWNERS10
-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/Annotations/Android.bp2
-rw-r--r--SafetyCenter/Config/Android.bp2
-rw-r--r--SafetyCenter/Config/TEST_MAPPING12
-rw-r--r--SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java25
-rw-r--r--SafetyCenter/Config/tests/Android.bp2
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt57
-rw-r--r--SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt17
-rw-r--r--SafetyCenter/Config/tests/overlay/Android.bp1
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml18
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml17
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml17
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml15
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml17
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml12
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml16
-rw-r--r--SafetyCenter/Config/tests/res/raw-v35/config_valid.xml197
-rw-r--r--SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml17
-rw-r--r--SafetyCenter/ConfigLintChecker/Android.bp118
-rw-r--r--SafetyCenter/ConfigLintChecker/java/android/os/Build.java4
-rw-r--r--SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java6
-rw-r--r--SafetyCenter/InternalData/Android.bp1
-rw-r--r--SafetyCenter/PendingIntents/Android.bp1
-rw-r--r--SafetyCenter/Persistence/Android.bp1
-rw-r--r--SafetyCenter/Persistence/tests/Android.bp1
-rw-r--r--SafetyCenter/Resources/Android.bp10
-rw-r--r--SafetyCenter/Resources/res/drawable-night-v35/illustration_android_cellular_network_security_sources.xml18
-rw-r--r--SafetyCenter/Resources/res/drawable-v35/illustration_android_cellular_network_security_sources.xml19
-rw-r--r--SafetyCenter/Resources/res/drawable-w480dp-night-v35/illustration_android_cellular_network_security_sources.xml19
-rw-r--r--SafetyCenter/Resources/res/drawable-w480dp-v35/illustration_android_cellular_network_security_sources.xml19
-rw-r--r--SafetyCenter/Resources/res/raw-v35/safety_center_config.xml152
-rw-r--r--SafetyCenter/Resources/res/values-af-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-am-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ar-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-as-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-az-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-b+sr+Latn-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-b+sr+Latn/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-be-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-bg-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-bn-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-bs-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-bs/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ca-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-cs-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-da-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-de-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-el-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-en-rAU-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-en-rCA-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-en-rGB-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-en-rIN-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-en-rXC-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-es-rUS-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-es-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-et-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-eu-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-eu-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-fa-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fa-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-fa/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fi-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-fi-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-fi/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-fr-rCA-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-fr-rCA/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-fr-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-fr/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-gl-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-gu-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-hi-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-hi-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-hr-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-hu-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-hy-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-hy-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-in-v34/strings.xml8
-rw-r--r--SafetyCenter/Resources/res/values-in-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-is-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-it-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-it/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-iw-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ja-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ka-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-kk-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-kk-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-km-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-km-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-kn-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ko-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ky-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ky/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-lo-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-lt-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-lv-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-mk-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ml-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-mn-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-mr-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-mr-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ms-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ms-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-my-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-my-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-my/strings.xml4
-rw-r--r--SafetyCenter/Resources/res/values-nb-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ne-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-nl-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-or-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-pa-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-pa-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-pl-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-pt-rBR-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-pt-rPT-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-pt-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ro-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ru-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-ru-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-si-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-sk-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-sl-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-sq-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-sr-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-sr/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-sv-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-sw-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-sw-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ta-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-te-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-th-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-th-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-tl-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-tr-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-uk-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-ur-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-uz-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-v34/config.xml2
-rw-r--r--SafetyCenter/Resources/res/values-v35/config.xml23
-rw-r--r--SafetyCenter/Resources/res/values-v35/strings.xml37
-rw-r--r--SafetyCenter/Resources/res/values-vi-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-zh-rCN-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-zh-rHK-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml2
-rw-r--r--SafetyCenter/Resources/res/values-zh-rTW-v35/strings.xml30
-rw-r--r--SafetyCenter/Resources/res/values-zu-v35/strings.xml30
-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.xml1
-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.xml3
-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/Android.bp1
-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/Android.bp1
-rw-r--r--SafetyCenter/ResourcesLib/tests/AndroidTest.xml2
-rw-r--r--SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/Android.bp1
-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--SafetyLabel/Android.bp2
-rw-r--r--SafetyLabel/tests/Android.bp1
-rw-r--r--TEST_MAPPING71
-rw-r--r--flags/Android.bp60
-rw-r--r--flags/flags.aconfig47
-rw-r--r--framework-s/Android.bp31
-rw-r--r--framework-s/api/current.txt1
-rw-r--r--framework-s/api/module-lib-current.txt9
-rw-r--r--framework-s/api/module-lib-lint-baseline.txt39
-rw-r--r--framework-s/api/system-current.txt18
-rw-r--r--framework-s/api/system-lint-baseline.txt37
-rw-r--r--framework-s/jarjar-rules.txt8
-rw-r--r--framework-s/java/android/app/ecm/EnhancedConfirmationFrameworkInitializer.java51
-rw-r--r--framework-s/java/android/app/ecm/EnhancedConfirmationManager.java356
-rw-r--r--framework-s/java/android/app/ecm/IEnhancedConfirmationManager.aidl33
-rw-r--r--framework-s/java/android/app/role/IRoleController.aidl2
-rw-r--r--framework-s/java/android/app/role/IRoleManager.aidl25
-rw-r--r--framework-s/java/android/app/role/RoleControllerManager.java104
-rw-r--r--framework-s/java/android/app/role/RoleControllerService.java147
-rw-r--r--framework-s/java/android/app/role/RoleManager.java189
-rw-r--r--framework-s/java/android/app/role/TEST_MAPPING36
-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.java124
-rw-r--r--framework-s/java/android/safetycenter/config/SafetySourcesGroup.java3
-rw-r--r--framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd223
-rw-r--r--framework-s/lint-baseline-framework-permission-s.xml26
-rw-r--r--framework-s/lint-baseline.xml22
-rw-r--r--framework/Android.bp10
-rw-r--r--framework/java/android/permission/PermissionStateUnused.java (renamed from framework/java/android/permission/PermissionState.java)2
-rw-r--r--ktfmt_includes.txt2
-rw-r--r--permissions/Android.bp8
-rw-r--r--permissions/com.android.permissioncontroller.xml1
-rw-r--r--service/Android.bp20
-rw-r--r--service/api/system-server-current.txt2
-rw-r--r--service/jarjar-rules.txt13
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationService.java397
-rw-r--r--service/java/com/android/ecm/EnhancedConfirmationStatsLogUtils.kt45
-rw-r--r--service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java4
-rw-r--r--service/java/com/android/permission/util/UserUtils.java35
-rw-r--r--service/java/com/android/role/LocalRoleController.java104
-rw-r--r--service/java/com/android/role/RemoteRoleController.java84
-rw-r--r--service/java/com/android/role/RoleController.java68
-rw-r--r--service/java/com/android/role/RoleService.java324
-rw-r--r--service/java/com/android/role/RoleUserState.java82
-rw-r--r--service/java/com/android/role/TEST_MAPPING44
-rw-r--r--service/java/com/android/role/persistence/RolesPersistenceImpl.java16
-rw-r--r--service/java/com/android/role/persistence/RolesState.java40
-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.java117
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterConfigReader.java146
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterDataChangeNotifier.java5
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterDataFactory.java320
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterFlags.java137
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterListeners.java22
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterRefreshTracker.java95
-rw-r--r--service/java/com/android/safetycenter/SafetyCenterService.java528
-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.java22
-rw-r--r--service/java/com/android/safetycenter/SafetySourcesGroups.java5
-rw-r--r--service/java/com/android/safetycenter/UserProfileGroup.java236
-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.java98
-rw-r--r--service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java40
-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.java43
-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.java58
-rw-r--r--service/java/com/android/safetycenter/data/SafetySourceStateCollectedLogger.java22
-rw-r--r--service/java/com/android/safetycenter/data/package-info.java5
-rw-r--r--service/java/com/android/safetycenter/logging/SafetyCenterPullAtomCallback.java45
-rw-r--r--service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java88
-rw-r--r--service/java/com/android/safetycenter/logging/package-info.java5
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationChannels.java92
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java46
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationReceiver.java44
-rw-r--r--service/java/com/android/safetycenter/notifications/SafetyCenterNotificationSender.java50
-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--service/lint-baseline.xml146
-rw-r--r--service/proguard.flags4
-rw-r--r--service/proto/role_service.proto3
-rw-r--r--testing/Android.bp1
-rw-r--r--tests/apex/Android.bp1
-rw-r--r--tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt51
-rw-r--r--tests/cts/permission/Android.bp130
-rw-r--r--tests/cts/permission/AndroidManifest.xml100
-rw-r--r--tests/cts/permission/AndroidTest.xml144
-rw-r--r--tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp35
-rw-r--r--tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml41
-rw-r--r--tests/cts/permission/AppThatAccessesLocationOnCommand/Android.bp40
-rw-r--r--tests/cts/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java68
-rw-r--r--tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl22
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionA/Android.bp32
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionA/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp32
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/AndroidManifest.xml25
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp32
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp32
-rw-r--r--tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionA/Android.bp32
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionA/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp31
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp31
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp31
-rw-r--r--tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp32
-rw-r--r--tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml38
-rw-r--r--tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt49
-rw-r--r--tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp36
-rw-r--r--tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml24
-rw-r--r--tests/cts/permission/AppThatHasNotificationListener/Android.bp39
-rw-r--r--tests/cts/permission/AppThatHasNotificationListener/AndroidManifest.xml33
-rw-r--r--tests/cts/permission/AppThatHasNotificationListener/src/android/permission/cts/appthathasnotificationlistener/CtsNotificationListenerService.java21
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermission30/Android.bp40
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermission30/AndroidManifest.xml35
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermission30/src/android/permission/cts/appthatrequestpermission/AccessBluetoothOnCommand.java161
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermission31/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermission31/AndroidManifest.xml34
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/AndroidManifest.xml40
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp32
-rw-r--r--tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/AndroidManifest.xml36
-rw-r--r--tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/AndroidManifest.xml29
-rw-r--r--tests/cts/permission/AppThatRequestContactsPermission15/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestContactsPermission15/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestContactsPermission16/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestContactsPermission16/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestCustomCameraPermission/Android.bp38
-rw-r--r--tests/cts/permission/AppThatRequestCustomCameraPermission/AndroidManifest.xml40
-rw-r--r--tests/cts/permission/AppThatRequestCustomCameraPermission/res/values/strings.xml20
-rw-r--r--tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java85
-rw-r--r--tests/cts/permission/AppThatRequestDevicePermissions/Android.bp32
-rw-r--r--tests/cts/permission/AppThatRequestDevicePermissions/AndroidManifest.xml41
-rw-r--r--tests/cts/permission/AppThatRequestDevicePermissions/src/android/permission/cts/appthatrequestpermission/RevokeSelfPermissionReceiver.kt37
-rw-r--r--tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp32
-rw-r--r--tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/AndroidManifest.xml32
-rw-r--r--tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission22/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission22/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission28/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission28/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission29/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission29/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission29v4/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/AndroidManifest.xml30
-rw-r--r--tests/cts/permission/AppThatRequestOneTimePermission/Android.bp36
-rw-r--r--tests/cts/permission/AppThatRequestOneTimePermission/AndroidManifest.xml36
-rw-r--r--tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/KeepAliveForegroundService.java72
-rw-r--r--tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/RequestPermission.java29
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandB/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandB/AndroidManifest.xml35
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandB/res/values/strings.xml19
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandB/src/android/permission/cts/appthatrequestpermission/RequestPermission.java30
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandC/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandC/AndroidManifest.xml35
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandC/res/values/strings.xml19
-rw-r--r--tests/cts/permission/AppThatRequestPermissionAandC/src/android/permission/cts/appthatrequestpermission/RequestPermission.java30
-rw-r--r--tests/cts/permission/AppThatRequestStoragePermission22/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestStoragePermission28/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestStoragePermission28/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestStoragePermission29/Android.bp31
-rw-r--r--tests/cts/permission/AppThatRequestStoragePermission29/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/AppThatRequestSystemAlertWindow22/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml24
-rw-r--r--tests/cts/permission/AppThatRequestSystemAlertWindow23/Android.bp33
-rw-r--r--tests/cts/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml24
-rw-r--r--tests/cts/permission/AppThatRunsRationaleTests/Android.bp35
-rw-r--r--tests/cts/permission/AppThatRunsRationaleTests/AndroidManifest.xml26
-rw-r--r--tests/cts/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java42
-rw-r--r--tests/cts/permission/AppToTestRevokeSelfPermission/Android.bp36
-rw-r--r--tests/cts/permission/AppToTestRevokeSelfPermission/AndroidManifest.xml31
-rw-r--r--tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java49
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp33
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml29
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp33
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml30
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp30
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/AndroidManifest.xml24
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestsPermissions/Android.bp30
-rw-r--r--tests/cts/permission/AppWithSharedUidThatRequestsPermissions/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/OWNERS12
-rw-r--r--tests/cts/permission/README16
-rw-r--r--tests/cts/permission/StorageEscalationApp28/Android.bp31
-rw-r--r--tests/cts/permission/StorageEscalationApp28/AndroidManifest.xml30
-rw-r--r--tests/cts/permission/StorageEscalationApp29Full/Android.bp31
-rw-r--r--tests/cts/permission/StorageEscalationApp29Full/AndroidManifest.xml30
-rw-r--r--tests/cts/permission/StorageEscalationApp29Scoped/Android.bp31
-rw-r--r--tests/cts/permission/StorageEscalationApp29Scoped/AndroidManifest.xml30
-rw-r--r--tests/cts/permission/jni/Android.bp60
-rw-r--r--tests/cts/permission/jni/CtsPermissionsJniOnLoad.cpp34
-rw-r--r--tests/cts/permission/jni/PermissionManagerNativeJniTest.cpp50
-rw-r--r--tests/cts/permission/jni/android_permission_cts_FileUtils.cpp250
-rw-r--r--tests/cts/permission/nativeTests/Android.bp56
-rw-r--r--tests/cts/permission/nativeTests/AndroidTest.xml43
-rw-r--r--tests/cts/permission/nativeTests/src/PermissionManagerNativeTest.cpp63
-rw-r--r--tests/cts/permission/permissionTestUtilLib/Android.bp37
-rw-r--r--tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerHelperRule.kt56
-rw-r--r--tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerService.java72
-rw-r--r--tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerServiceUtils.kt127
-rw-r--r--tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/MtsIgnore.java40
-rw-r--r--tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/PermissionUtils.java414
-rw-r--r--tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/TestUtils.java205
-rw-r--r--tests/cts/permission/res/drawable/robot.pngbin0 -> 5634 bytes
-rw-r--r--tests/cts/permission/res/values/strings.xml22
-rw-r--r--tests/cts/permission/res/xml/test_accessibilityservice.xml21
-rw-r--r--tests/cts/permission/sdk28/Android.bp33
-rw-r--r--tests/cts/permission/sdk28/AndroidManifest.xml52
-rw-r--r--tests/cts/permission/sdk28/AndroidTest.xml32
-rw-r--r--tests/cts/permission/sdk28/OWNERS1
-rw-r--r--tests/cts/permission/sdk28/TEST_MAPPING7
-rw-r--r--tests/cts/permission/sdk28/res/values/strings.xml19
-rw-r--r--tests/cts/permission/sdk28/src/android/permission/cts/sdk28/RequestLocation.java70
-rw-r--r--tests/cts/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt317
-rw-r--r--tests/cts/permission/src/android/permission/cts/AccessibilityTestService.kt22
-rw-r--r--tests/cts/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java135
-rw-r--r--tests/cts/permission/src/android/permission/cts/AppIdleStatePermissionTest.java92
-rw-r--r--tests/cts/permission/src/android/permission/cts/AppWidgetManagerPermissionTest.java62
-rw-r--r--tests/cts/permission/src/android/permission/cts/BackgroundPermissionButtonLabelTest.java55
-rw-r--r--tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java254
-rw-r--r--tests/cts/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java318
-rw-r--r--tests/cts/permission/src/android/permission/cts/Camera2PermissionTest.java191
-rw-r--r--tests/cts/permission/src/android/permission/cts/CameraPermissionTest.java95
-rw-r--r--tests/cts/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java120
-rw-r--r--tests/cts/permission/src/android/permission/cts/ContactsProviderTest.java158
-rw-r--r--tests/cts/permission/src/android/permission/cts/DebuggableTest.java50
-rw-r--r--tests/cts/permission/src/android/permission/cts/DevicePermissionsTest.kt562
-rw-r--r--tests/cts/permission/src/android/permission/cts/DuplicatePermissionDefinitionsTest.kt195
-rw-r--r--tests/cts/permission/src/android/permission/cts/EthernetManagerPermissionTest.java113
-rw-r--r--tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java1279
-rw-r--r--tests/cts/permission/src/android/permission/cts/FileUtils.java128
-rw-r--r--tests/cts/permission/src/android/permission/cts/IgnoreAllTestsRule.java52
-rw-r--r--tests/cts/permission/src/android/permission/cts/LocationAccessCheckTest.java794
-rw-r--r--tests/cts/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java68
-rw-r--r--tests/cts/permission/src/android/permission/cts/MinMaxSdkVersionTest.kt97
-rw-r--r--tests/cts/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java307
-rw-r--r--tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java315
-rw-r--r--tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java221
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java99
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoAudioPermissionTest.java124
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoBroadcastPackageRemovedPermissionTest.java104
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoCaptureVideoPermissionTest.java106
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoKeyPermissionTest.java100
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java99
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoReadLogsPermissionTest.java104
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoRollbackPermissionTest.java48
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoSystemFunctionPermissionTest.java182
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoWakeLockPermissionTest.java119
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoWallpaperPermissionsTest.java190
-rw-r--r--tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java219
-rw-r--r--tests/cts/permission/src/android/permission/cts/NotificationListenerCheckTest.java226
-rw-r--r--tests/cts/permission/src/android/permission/cts/NotificationListenerCheckWithSafetyCenterUnsupportedTest.java108
-rw-r--r--tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java396
-rw-r--r--tests/cts/permission/src/android/permission/cts/PackageManagerRequiringPermissionsTest.java124
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionControllerTest.java517
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java267
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionGroupChange.java207
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionManagerNativeJniTest.java26
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionManagerTest.java52
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionStubActivity.java46
-rw-r--r--tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java278
-rw-r--r--tests/cts/permission/src/android/permission/cts/PlatformPermissionGroupMappingTest.kt62
-rw-r--r--tests/cts/permission/src/android/permission/cts/PowerManagerServicePermissionTest.java57
-rw-r--r--tests/cts/permission/src/android/permission/cts/ProviderPermissionTest.java294
-rw-r--r--tests/cts/permission/src/android/permission/cts/RebootPermissionTest.java49
-rw-r--r--tests/cts/permission/src/android/permission/cts/RecordSensitiveContentPermissionTest.kt62
-rw-r--r--tests/cts/permission/src/android/permission/cts/RemovePermissionTest.java247
-rw-r--r--tests/cts/permission/src/android/permission/cts/RevokePermissionTest.kt183
-rw-r--r--tests/cts/permission/src/android/permission/cts/RevokeSawPermissionTest.kt65
-rw-r--r--tests/cts/permission/src/android/permission/cts/RevokeSelfPermissionTest.java361
-rw-r--r--tests/cts/permission/src/android/permission/cts/RuntimePermissionPresentationInfoTest.java64
-rw-r--r--tests/cts/permission/src/android/permission/cts/SafetyCenterUtils.kt158
-rw-r--r--tests/cts/permission/src/android/permission/cts/SdkSandboxPermissionTest.java64
-rw-r--r--tests/cts/permission/src/android/permission/cts/SecureElementPermissionTest.java66
-rw-r--r--tests/cts/permission/src/android/permission/cts/ServicePermissionTest.java75
-rw-r--r--tests/cts/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java85
-rw-r--r--tests/cts/permission/src/android/permission/cts/SharedUidPermissionsTest.java150
-rw-r--r--tests/cts/permission/src/android/permission/cts/ShellCommandPermissionTest.java61
-rw-r--r--tests/cts/permission/src/android/permission/cts/ShellPermissionTest.java95
-rw-r--r--tests/cts/permission/src/android/permission/cts/SmsManagerPermissionTest.java124
-rw-r--r--tests/cts/permission/src/android/permission/cts/SplitPermissionTest.java584
-rwxr-xr-xtests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java174
-rw-r--r--tests/cts/permission/src/android/permission/cts/StorageEscalationTest.kt158
-rw-r--r--tests/cts/permission/src/android/permission/cts/TvPermissionTest.java116
-rw-r--r--tests/cts/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt243
-rw-r--r--tests/cts/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl22
-rw-r--r--tests/cts/permission/telephony/Android.bp38
-rw-r--r--tests/cts/permission/telephony/AndroidManifest.xml37
-rw-r--r--tests/cts/permission/telephony/AndroidTest.xml44
-rw-r--r--tests/cts/permission/telephony/OWNERS3
-rw-r--r--tests/cts/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java470
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml19
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml25
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml27
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml25
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml25
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml28
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml19
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml25
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp32
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml25
-rw-r--r--tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml19
-rw-r--r--tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/Android.bp33
-rw-r--r--tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/src/android/permissionmultidevice/cts/accessremotedevicecamera/RequestPermissionActivity.kt64
-rw-r--r--tests/cts/permissionmultidevice/Android.bp52
-rw-r--r--tests/cts/permissionmultidevice/AndroidManifest.xml37
-rw-r--r--tests/cts/permissionmultidevice/AndroidTest.xml88
-rw-r--r--tests/cts/permissionmultidevice/OWNERS3
-rw-r--r--tests/cts/permissionmultidevice/TEST_MAPPING7
-rw-r--r--tests/cts/permissionmultidevice/TestUtils/Android.bp41
-rw-r--r--tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PackageManagementUtils.kt54
-rw-r--r--tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PermissionUtils.kt74
-rw-r--r--tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/TestConstants.kt23
-rw-r--r--tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/UiAutomatorUtils.kt72
-rw-r--r--tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt448
-rw-r--r--tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt278
-rw-r--r--tests/cts/permissionmultiuser/Android.bp48
-rw-r--r--tests/cts/permissionmultiuser/AndroidManifest.xml34
-rw-r--r--tests/cts/permissionmultiuser/AndroidTest.xml71
-rw-r--r--tests/cts/permissionmultiuser/OWNERS3
-rw-r--r--tests/cts/permissionmultiuser/RequestLocationApp/Android.bp25
-rw-r--r--tests/cts/permissionmultiuser/RequestLocationApp/AndroidManifest.xml23
-rw-r--r--tests/cts/permissionmultiuser/TEST_MAPPING7
-rw-r--r--tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt493
-rw-r--r--tests/cts/permissionpolicy/Android.bp67
-rwxr-xr-xtests/cts/permissionpolicy/AndroidManifest.xml74
-rw-r--r--tests/cts/permissionpolicy/AndroidTest.xml90
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml26
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml26
-rw-r--r--tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/Android.bp30
-rw-r--r--tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/AndroidManifest.xml29
-rw-r--r--tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/Android.bp30
-rw-r--r--tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionpolicy/CtsProcessOutgoingCalls/Android.bp33
-rw-r--r--tests/cts/permissionpolicy/CtsProcessOutgoingCalls/AndroidManifest.xml33
-rw-r--r--tests/cts/permissionpolicy/CtsProcessOutgoingCalls/src/android/permissionpolicy/cts/receivecallbroadcast/ProcessOutgoingCallReceiver.kt36
-rw-r--r--tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/Android.bp30
-rw-r--r--tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/AndroidManifest.xml40
-rw-r--r--tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/Android.bp30
-rw-r--r--tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/AndroidManifest.xml38
-rw-r--r--tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml26
-rw-r--r--tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml26
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/AndroidManifest.xml31
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/Android.bp24
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/AndroidManifest.xml30
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/Android.bp24
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/AndroidManifest.xml30
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/AndroidManifest.xml30
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/AndroidManifest.xml32
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/Android.bp24
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/AndroidManifest.xml31
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/Android.bp24
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/AndroidManifest.xml31
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/Android.bp26
-rw-r--r--tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/AndroidManifest.xml31
-rw-r--r--tests/cts/permissionpolicy/OWNERS8
-rw-r--r--tests/cts/permissionpolicy/res/raw/OWNERS8
-rw-r--r--tests/cts/permissionpolicy/res/raw/android_manifest.xml8842
-rw-r--r--tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml623
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/CommandBroadcastReceiver.java49
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ContactsProviderTest.java95
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/IntelligenceRolesPolicyTest.java99
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoCaptureAudioOutputPermissionTest.java70
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoProcessOutgoingCallPermissionTest.java142
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoReceiveSmsPermissionTest.java293
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoWriteSecureSettingsPermissionTest.java47
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionMaxSdkVersionTest.java59
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java551
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PrivappPermissionsTest.java251
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ProtectedBroadcastsTest.java121
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedPermissionsTest.java745
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionSharedUidTest.java269
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionTest.java755
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt192
-rw-r--r--tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt145
-rw-r--r--tests/cts/permissionui/Android.bp87
-rw-r--r--tests/cts/permissionui/AndroidManifest.xml89
-rw-r--r--tests/cts/permissionui/AndroidTest.xml104
-rw-r--r--tests/cts/permissionui/AppThatAccessesCameraAndMic/Android.bp35
-rw-r--r--tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml36
-rw-r--r--tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values-watch/styles.xml25
-rw-r--r--tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values/styles.xml19
-rw-r--r--tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt275
-rw-r--r--tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/Android.bp33
-rw-r--r--tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/AndroidManifest.xml39
-rw-r--r--tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity1.kt39
-rw-r--r--tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity2.kt30
-rw-r--r--tests/cts/permissionui/CreateNotificationChannelsApp31/Android.bp33
-rw-r--r--tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml44
-rw-r--r--tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt187
-rw-r--r--tests/cts/permissionui/DifferentPkgNameApp/Android.bp33
-rw-r--r--tests/cts/permissionui/DifferentPkgNameApp/AndroidManifest.xml33
-rw-r--r--tests/cts/permissionui/DifferentPkgNameApp/src/android/permissionui/cts/usepermissionother/EmptyActivity.kt21
-rw-r--r--tests/cts/permissionui/HelperAppOverlay/Android.bp32
-rw-r--r--tests/cts/permissionui/HelperAppOverlay/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionui/HelperAppOverlay/src/android/permissionui/cts/helper/overlay/OverlayActivity.kt26
-rw-r--r--tests/cts/permissionui/ImplicitUserSelectStorageApp/Android.bp33
-rw-r--r--tests/cts/permissionui/ImplicitUserSelectStorageApp/AndroidManifest.xml30
-rw-r--r--tests/cts/permissionui/MediaPermissionApp33WithStorage/Android.bp32
-rw-r--r--tests/cts/permissionui/MediaPermissionApp33WithStorage/AndroidManifest.xml29
-rw-r--r--tests/cts/permissionui/OWNERS4
-rw-r--r--tests/cts/permissionui/PermissionPolicyApp25/Android.bp31
-rw-r--r--tests/cts/permissionui/PermissionPolicyApp25/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionui/PermissionPolicyApp25/src/android/permissionui/cts/permissionpolicy/TestProtectionFlagsActivity.kt112
-rw-r--r--tests/cts/permissionui/StorageApp33/Android.bp33
-rw-r--r--tests/cts/permissionui/StorageApp33/AndroidManifest.xml30
-rw-r--r--tests/cts/permissionui/TEST_MAPPING42
-rw-r--r--tests/cts/permissionui/UsePermissionApp22/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp22/AndroidManifest.xml79
-rw-r--r--tests/cts/permissionui/UsePermissionApp22CalendarOnly/Android.bp33
-rw-r--r--tests/cts/permissionui/UsePermissionApp22CalendarOnly/AndroidManifest.xml36
-rw-r--r--tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/CheckPermissionService.kt39
-rw-r--r--tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/StartCheckPermissionServiceActivity.kt31
-rw-r--r--tests/cts/permissionui/UsePermissionApp22None/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp22None/AndroidManifest.xml30
-rw-r--r--tests/cts/permissionui/UsePermissionApp23/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp23/AndroidManifest.xml76
-rw-r--r--tests/cts/permissionui/UsePermissionApp25/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp25/AndroidManifest.xml76
-rw-r--r--tests/cts/permissionui/UsePermissionApp26/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp26/AndroidManifest.xml34
-rw-r--r--tests/cts/permissionui/UsePermissionApp28/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp28/AndroidManifest.xml32
-rw-r--r--tests/cts/permissionui/UsePermissionApp29/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp29/AndroidManifest.xml33
-rw-r--r--tests/cts/permissionui/UsePermissionApp30/Android.bp33
-rw-r--r--tests/cts/permissionui/UsePermissionApp30/AndroidManifest.xml43
-rw-r--r--tests/cts/permissionui/UsePermissionApp30WithBackground/Android.bp32
-rw-r--r--tests/cts/permissionui/UsePermissionApp30WithBackground/AndroidManifest.xml31
-rw-r--r--tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt57
-rw-r--r--tests/cts/permissionui/UsePermissionApp30WithBluetooth/Android.bp34
-rw-r--r--tests/cts/permissionui/UsePermissionApp30WithBluetooth/AndroidManifest.xml38
-rw-r--r--tests/cts/permissionui/UsePermissionApp30WithBluetooth/src/android/permissionui/cts/usepermission/AccessBluetoothOnCommand.kt154
-rw-r--r--tests/cts/permissionui/UsePermissionApp31/Android.bp33
-rw-r--r--tests/cts/permissionui/UsePermissionApp31/AndroidManifest.xml31
-rw-r--r--tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp35
-rw-r--r--tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml33
-rw-r--r--tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata17
-rw-r--r--tests/cts/permissionui/UsePermissionApp32/Android.bp34
-rw-r--r--tests/cts/permissionui/UsePermissionApp32/AndroidManifest.xml26
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatest/Android.bp40
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml56
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/CheckCalendarAccessActivity.kt71
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/FinishOnCreateActivity.kt29
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt93
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatestNone/Android.bp33
-rw-r--r--tests/cts/permissionui/UsePermissionAppLatestNone/AndroidManifest.xml28
-rw-r--r--tests/cts/permissionui/UsePermissionAppLocationProvider/Android.bp31
-rw-r--r--tests/cts/permissionui/UsePermissionAppLocationProvider/AndroidManifest.xml37
-rw-r--r--tests/cts/permissionui/UsePermissionAppLocationProvider/res/values/strings.xml (renamed from PermissionController/res/values-w764dp-v33/dimens.xml)6
-rw-r--r--tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/AddLocationProviderActivity.kt46
-rw-r--r--tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/UseMicrophoneActivity.kt68
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/Android.bp35
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/AndroidManifest.xml29
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/res/drawable/border.xml25
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml34
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/strings.xml19
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/styles.xml22
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/OverlayActivity.kt60
-rw-r--r--tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt106
-rw-r--r--tests/cts/permissionui/res/raw/lg_g4_iso_800_jpg.jpgbin0 -> 107684 bytes
-rw-r--r--tests/cts/permissionui/res/raw/test_video.mp4bin0 -> 135632 bytes
-rwxr-xr-x[-rw-r--r--]tests/cts/permissionui/res/values-en-rGB/strings.xml (renamed from PermissionController/res/values-w764dp-v34/dimens.xml)6
-rwxr-xr-xtests/cts/permissionui/res/values/strings.xml28
-rw-r--r--tests/cts/permissionui/res/xml/test_accessibilityservice.xml21
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt727
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/AppMetadata.kt214
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt381
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt515
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt1454
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt757
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt355
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/LocationAccuracyTest.kt164
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/LocationProviderInterceptDialogTest.kt149
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionTest.kt183
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionUpgradeTest.kt60
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/NoPermissionTest.kt46
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt416
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionDecisionsTest.kt131
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionGroupTest.kt90
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionNoOpGtsTest.kt29
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionPolicyTest25.kt64
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt359
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionRationaleTest.kt385
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt98
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTest.kt176
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt122
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionTapjackingTest.kt150
-rwxr-xr-xtests/cts/permissionui/src/android/permissionui/cts/PermissionTest22.kt66
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionTest23.kt417
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionTest29.kt241
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30.kt93
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30WithBluetooth.kt200
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionUpgradeTest.kt154
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PermissionUsageInfoTest.kt56
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerPermissionTest.kt523
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerUtils.kt128
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt241
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt622
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/SafetyProtectionTest.kt120
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt182
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt62
-rw-r--r--tests/cts/permissionui/src/android/permissionui/cts/TestInstallerActivity.kt21
-rw-r--r--tests/cts/role/Android.bp53
-rw-r--r--tests/cts/role/AndroidManifest.xml40
-rw-r--r--tests/cts/role/AndroidTest.xml52
-rw-r--r--tests/cts/role/CtsRoleTestApp/Android.bp26
-rw-r--r--tests/cts/role/CtsRoleTestApp/AndroidManifest.xml131
-rw-r--r--tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java52
-rw-r--r--tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java52
-rw-r--r--tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/IsRoleHeldActivity.java46
-rw-r--r--tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java53
-rw-r--r--tests/cts/role/CtsRoleTestApp28/Android.bp27
-rw-r--r--tests/cts/role/CtsRoleTestApp28/AndroidManifest.xml79
-rw-r--r--tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java40
-rw-r--r--tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java41
-rw-r--r--tests/cts/role/CtsRoleTestApp33WithoutInCallService/Android.bp23
-rw-r--r--tests/cts/role/CtsRoleTestApp33WithoutInCallService/AndroidManifest.xml37
-rw-r--r--tests/cts/role/CtsRoleTestAppForProfile/Android.bp23
-rw-r--r--tests/cts/role/CtsRoleTestAppForProfile/AndroidManifest.xml36
-rw-r--r--tests/cts/role/OWNERS3
-rw-r--r--tests/cts/role/TEST_MAPPING65
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleControllerManagerTest.kt167
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleManagerTest.java1437
-rw-r--r--tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt200
-rw-r--r--tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java75
-rw-r--r--tests/cts/safetycenter/Android.bp3
-rw-r--r--tests/cts/safetycenter/AndroidTest.xml3
-rw-r--r--tests/cts/safetycenter/TEST_MAPPING12
-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.kt116
-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/Android.bp2
-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.kt852
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/Android.bp1
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/AndroidTest.xml3
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/TEST_MAPPING12
-rw-r--r--tests/functional/safetycenter/safetycenteractivity/src/android/safetycenter/functional/ui/SafetyCenterActivityTest.kt107
-rw-r--r--tests/functional/safetycenter/singleuser/Android.bp1
-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/TEST_MAPPING12
-rw-r--r--tests/functional/safetycenter/singleuser/src/android/safetycenter/functional/SafetyCenterManagerTest.kt1394
-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_MAPPING27
-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.bp4
-rw-r--r--tests/hostside/safetycenter/AndroidTest.xml3
-rw-r--r--tests/hostside/safetycenter/TEST_MAPPING7
-rw-r--r--tests/hostside/safetycenter/helper-app/Android.bp4
-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/Android.bp3
-rw-r--r--tests/utils/safetycenter/AndroidManifest.xml28
-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.kt89
-rw-r--r--tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestData.kt58
-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.kt286
-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
-rw-r--r--tests/utils/safetycenter/res/values/styles.xml24
1732 files changed, 107196 insertions, 17311 deletions
diff --git a/Android.bp b/Android.bp
index 4a436da6c..9b1857741 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
default_visibility: [":__subpackages__"],
}
@@ -21,6 +22,10 @@ apex {
name: "com.android.permission",
defaults: ["com.android.permission-defaults"],
manifest: "apex_manifest.json",
+ compat_configs: [
+ "framework-permission-s-compat-config",
+ ],
+ visibility: ["//packages/modules/common/build"],
}
apex_defaults {
@@ -30,7 +35,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",
@@ -67,8 +71,8 @@ sdk {
bootclasspath_fragment {
name: "com.android.permission-bootclasspath-fragment",
contents: [
- "framework-permission",
- "framework-permission-s",
+ "framework-permission",
+ "framework-permission-s",
],
apex_available: ["com.android.permission"],
@@ -105,6 +109,7 @@ bootclasspath_fragment {
// result in a build failure due to inconsistent flags.
package_prefixes: [
"android.app.role",
+ "android.app.ecm",
"android.permission.jarjar",
"android.safetycenter",
"android.safetylabel",
diff --git a/OWNERS b/OWNERS
index 8f1443bc6..403e9f430 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 137825
+
include platform/frameworks/base:/core/java/android/permission/OWNERS
include platform/packages/modules/common:/MODULES_OWNERS # see go/mainline-owners-policy
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 925bbf9d7..0bfd581d5 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,14 @@
[Builtin Hooks]
xmllint = true
commit_msg_changeid_field = true
+ktfmt = true
+
+[Builtin Hooks Options]
+ktfmt = --kotlinlang-style --include-dirs=SafetyCenter,PermissionController,tests
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/packages/modules/Permission/ktfmt_includes.txt ${PREUPLOAD_FILES}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
+
+[Tool Paths]
+ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/PermissionController/Android.bp b/PermissionController/Android.bp
index 9b20819f0..cd1e75222 100644
--- a/PermissionController/Android.bp
+++ b/PermissionController/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: [
"packages_modules_Permission_PermissionController_license",
],
@@ -37,8 +38,8 @@ genrule {
name: "statslog-permissioncontroller-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module permissioncontroller" +
- " --javaPackage com.android.permissioncontroller" +
- " --javaClass PermissionControllerStatsLog --minApiLevel 30",
+ " --javaPackage com.android.permissioncontroller" +
+ " --javaClass PermissionControllerStatsLog --minApiLevel 30",
out: ["com/android/permissioncontroller/PermissionControllerStatsLog.java"],
}
@@ -54,54 +55,48 @@ java_library {
],
}
-// File to be included by permission controller app an mocking tests
-filegroup {
- name: "permissioncontroller-sources",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ":permissioncontroller-protos",
- ],
-}
-
-filegroup {
+java_library {
name: "permissioncontroller-protos",
+ sdk_version: "system_current",
+ min_sdk_version: "30",
srcs: [
"src/**/*.proto",
],
+ proto: {
+ type: "lite",
+ include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"],
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
}
-android_app {
- name: "PermissionController",
- // Compiling against "module_current" would allow using non-APIs within the permission APEX
- // boundaries, which may be unsafe because PermissionController is also shipped as a standalone
- // artifact. See also b/209458854.
+android_library {
+ name: "PermissionController-lib",
sdk_version: "system_current",
min_sdk_version: "30",
- updatable: true,
- privileged: true,
- certificate: "platform",
- rename_resources_package: false,
- required: ["privapp_allowlist_com.android.permissioncontroller.xml"],
-
- srcs: [":permissioncontroller-sources"],
+ use_resource_processor: true,
+ manifest: "AndroidManifest-lib.xml",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
//javacflags: ["-Werror"],
kotlincflags: [
"-Werror",
"-opt-in=kotlinx.coroutines.DelicateCoroutinesApi",
- "-Xjvm-default=all"
+ "-Xjvm-default=all",
],
libs: [
"android.car-stubs",
- // Soong fails to automatically add this dependency because all the
- // *.kt sources are inside a filegroup.
- "kotlin-annotations",
"safety-center-annotations",
],
static_libs: [
+ "permissioncontroller-protos",
"iconloader_sc_mainline_prod",
"com.google.android.material_material",
"androidx.transition_transition",
@@ -117,7 +112,6 @@ android_app {
"androidx.legacy_legacy-preference-v14",
"androidx.leanback_leanback",
"androidx.leanback_leanback-preference",
- "androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-common-java8",
"kotlin-stdlib",
"kotlinx-coroutines-android",
@@ -152,18 +146,53 @@ android_app {
"lottie",
"safety-label",
"role-controller",
+ "android.permission.flags-aconfig-java-export",
+ "com.android.permission.flags-aconfig-java-export",
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.runtime_runtime-livedata",
+ "androidx.compose.ui_ui",
+ "androidx.wear.compose_compose-material",
+ "android.content.pm.flags-aconfig-java-export",
+ "android.os.flags-aconfig-java-export",
],
- proto: {
- type: "lite",
- include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"],
+ lint: {
+ error_checks: ["Recycle"],
+ baseline_filename: "lint-baseline.xml",
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+
+ // TODO(b/313706381): Remove jarjar once flagging lib is fixed
+ jarjar_rules: "jarjar-rules.txt",
+}
+
+android_app {
+ name: "PermissionController",
+ // Compiling against "module_current" would allow using non-APIs within the permission APEX
+ // boundaries, which may be unsafe because PermissionController is also shipped as a standalone
+ // artifact. See also b/209458854.
+ sdk_version: "system_current",
+ min_sdk_version: "30",
+ updatable: true,
+ privileged: true,
+ certificate: "platform",
+ use_resource_processor: true,
+ rename_resources_package: false,
+ privapp_allowlist: ":privapp_allowlist_com.android.permissioncontroller.xml",
+
+ static_libs: ["PermissionController-lib"],
+
lint: {
- strict_updatability_linting: true,
+ error_checks: ["Recycle"],
},
optimize: {
+ proguard_compatibility: false, // TODO(b/215530220): remove when this is default behavior
proguard_flags_files: ["proguard.flags"],
},
diff --git a/PermissionController/AndroidManifest-lib.xml b/PermissionController/AndroidManifest-lib.xml
new file mode 100644
index 000000000..759256823
--- /dev/null
+++ b/PermissionController/AndroidManifest-lib.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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 package="com.android.permissioncontroller">
+</manifest>
diff --git a/PermissionController/AndroidManifest.xml b/PermissionController/AndroidManifest.xml
index 874ba35d6..c0d6c2907 100644
--- a/PermissionController/AndroidManifest.xml
+++ b/PermissionController/AndroidManifest.xml
@@ -53,7 +53,6 @@
<uses-permission android:name="android.permission.START_VIEW_PERMISSION_USAGE" />
<uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" />
<uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
- <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.MANAGE_SAFETY_CENTER" />
@@ -66,6 +65,10 @@
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
<uses-permission android:name="android.permission.GET_APP_METADATA" />
+ <uses-permission android:name="android.permission.NFC_PREFERRED_PAYMENT_INFO" />
+ <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
+ <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
+ <uses-permission android:name="android.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION" />
<application android:name="com.android.permissioncontroller.PermissionControllerApplication"
android:label="@string/app_name"
@@ -270,8 +273,7 @@
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing"
android:visibleToInstantApps="true"
android:inheritShowWhenLocked="true"
- android:hardwareAccelerated="false"
- android:canDisplayOnRemoteDevices="false">
+ android:canDisplayOnRemoteDevices="@bool/is_at_least_v">
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
@@ -282,6 +284,26 @@
</intent-filter>
</activity>
+ <activity android:name="com.android.permissioncontroller.permission.ui.PermissionDialogStreamingBlockedActivity"
+ android:theme="@style/PermissionDialog.FilterTouches"
+ android:excludeFromRecents="true"
+ android:exported="false"
+ android:enabled="@bool/is_at_least_v">
+ </activity>
+
+ <activity android:name="com.android.permissioncontroller.ecm.EnhancedConfirmationDialogActivity"
+ android:theme="@style/Theme.EnhancedConfirmationDialog.FilterTouches"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:launchMode="singleTop"
+ android:permission="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES"
+ android:enabled="@bool/is_at_least_v">
+ <intent-filter android:priority="1">
+ <action android:name="android.app.ecm.action.SHOW_ECM_RESTRICTED_SETTING_DIALOG" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name="com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity"
android:configChanges="keyboardHidden|screenSize"
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing"
@@ -291,7 +313,7 @@
android:visibleToInstantApps="true"
android:inheritShowWhenLocked="true"
android:hardwareAccelerated="false"
- android:canDisplayOnRemoteDevices="false">
+ android:canDisplayOnRemoteDevices="@bool/is_at_least_v">
</activity>
<activity android:name="com.android.permissioncontroller.permission.ui.ManagePermissionsActivity"
@@ -399,6 +421,7 @@
<activity android:name="com.android.permissioncontroller.role.ui.RequestRoleActivity"
android:excludeFromRecents="true"
android:exported="true"
+ android:launchMode="singleTop"
android:theme="@style/RequestRole.FilterTouches">
<intent-filter android:priority="1">
<action android:name="android.app.role.action.REQUEST_ROLE" />
@@ -472,6 +495,18 @@
</intent-filter>
</activity>
+ <activity android:name="com.android.permissioncontroller.role.ui.v35.ChangeDefaultCardEmulationActivity"
+ android:enabled="@bool/is_at_least_v"
+ android:excludeFromRecents="true"
+ android:noHistory="true"
+ android:exported="true"
+ android:theme="@android:style/Theme.NoDisplay">
+ <intent-filter android:priority="1001">
+ <action android:name="android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<provider android:name="com.android.permissioncontroller.permission.service.PermissionSearchIndexablesProvider"
android:authorities="com.android.permissioncontroller"
android:multiprocess="false"
@@ -521,7 +556,7 @@
</intent-filter>
</service>
- <service android:name="com.android.permissioncontroller.role.service.RoleControllerServiceImpl"
+ <service android:name="com.android.role.controller.service.RoleControllerServiceImpl"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.app.role.RoleControllerService"/>
@@ -577,7 +612,7 @@
</receiver>
<activity android:name="com.android.permissioncontroller.incident.ConfirmationActivity"
- android:theme="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight"
+ android:theme="@style/Theme.PermissionController.IncidentReportDialog"
android:exported="false"
android:excludeFromRecents="true"
android:finishOnCloseSystemDialogs="true"
@@ -593,6 +628,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"/>
@@ -606,8 +642,6 @@
</intent-filter>
</activity>
- <!-- Unexported empty activity for in-process tests -->
- <activity android:name="android.app.Activity" />
</application>
</manifest>
diff --git a/PermissionController/OWNERS b/PermissionController/OWNERS
index 5cd46d618..3872198cb 100644
--- a/PermissionController/OWNERS
+++ b/PermissionController/OWNERS
@@ -4,11 +4,20 @@ include platform/frameworks/base:/core/java/android/permission/OWNERS
stenning@google.com
# For television related changes
-aabdagic@google.com
-robhor@google.com
+per-file TELEVISION_OWNERS = file:/PermissionController/TELEVISION_OWNERS
+per-file src/com/android/permissioncontroller/permission/ui/television/** = file:/PermissionController/TELEVISION_OWNERS
+per-file res/*-television/* = file:/PermissionController/TELEVISION_OWNERS
+per-file tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/** = file:/PermissionController/TELEVISION_OWNERS
# For incident report related changes
joeo@google.com
# for SafetyCenter UI changes
per-file res/** = file:platform/packages/modules/Permission:/SafetyCenter/OWNERS
+
+# For Wear related changes
+per-file WEAR_OWNERS = file:/PermissionController/WEAR_OWNERS
+per-file src/com/android/permissioncontroller/permission/ui/wear/** = file:/PermissionController/WEAR_OWNERS
+per-file src/com/android/permissioncontroller/role/ui/wear/** = file:/PermissionController/WEAR_OWNERS
+per-file res/*-watch/* = file:/PermissionController/WEAR_OWNERS
+per-file tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/wear/** = file:/PermissionController/WEAR_OWNERS
diff --git a/PermissionController/TELEVISION_OWNERS b/PermissionController/TELEVISION_OWNERS
new file mode 100644
index 000000000..af786d751
--- /dev/null
+++ b/PermissionController/TELEVISION_OWNERS
@@ -0,0 +1,2 @@
+bronger@google.com
+timurc@google.com \ No newline at end of file
diff --git a/PermissionController/TEST_MAPPING b/PermissionController/TEST_MAPPING
index 0ae3818fd..a34a16034 100644
--- a/PermissionController/TEST_MAPPING
+++ b/PermissionController/TEST_MAPPING
@@ -16,17 +16,17 @@
"name": "PermissionUiTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"presubmit-large": [
{
- "name": "CtsPermission3TestCases",
+ "name": "CtsPermissionUiTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -43,7 +43,19 @@
"exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
},
{
- "exclude-annotation": "androidx.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ],
+ "file_patterns": ["res/xml/roles\\.xml"]
+ },
+ {
+ "name": "PermissionControllerMockingTests[com.google.android.permission.apex]",
+ "options": [
+ {
+ "include-filter": "com.android.permissioncontroller.tests.mocking.role.model.RoleParserTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
],
"file_patterns": ["res/xml/roles\\.xml"]
@@ -52,7 +64,69 @@
"name": "PermissionUiTestCases[com.google.android.permission.apex]",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ // TODO(b/238773220): These tests currently fails on R base image
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsGranted"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsRevoked"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenAppGetsInstalled"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenDefinerGetsUninstalled"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenUserGetsUninstalled"
+ }
+ ]
+ },
+ {
+ "name": "CtsPermissionUiTestCases[com.google.android.permission.apex]",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ],
+ "file_patterns": ["res/xml/roles\\.xml"]
+ },
+ {
+ "name": "PermissionControllerMockingTests",
+ "options": [
+ {
+ "include-filter": "com.android.permissioncontroller.tests.mocking.role.model.RoleParserTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ],
+ "file_patterns": ["res/xml/roles\\.xml"]
+ },
+ {
+ "name": "PermissionUiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
// TODO(b/238773220): These tests currently fails on R base image
{
@@ -73,14 +147,74 @@
]
},
{
- "name": "CtsPermission3TestCases[com.google.android.permission.apex]",
+ "name": "CtsPermissionUiTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
+ "postsubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "file_patterns": ["res/xml/roles\\.xml"]
+ },
+ {
+ "name": "PermissionUiTestCases"
+ },
+ {
+ "name": "CtsPermissionUiTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ }
+ ],
+ "file_patterns": ["res/xml/roles\\.xml"]
+ },
+ {
+ "name": "PermissionControllerMockingTests[com.google.android.permission.apex]",
+ "options": [
+ {
+ "include-filter": "com.android.permissioncontroller.tests.mocking.role.model.RoleParserTest"
+ }
+ ],
+ "file_patterns": ["res/xml/roles\\.xml"]
+ },
+ {
+ "name": "PermissionUiTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238773220): These tests currently fails on R base image
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsGranted"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageCustomPermissionsFragmentTest#groupSummaryGetsUpdatedWhenPermissionGetsRevoked"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenAppGetsInstalled"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenDefinerGetsUninstalled"
+ },
+ {
+ "exclude-filter": "com.android.permissioncontroller.permissionui.ui.handheld.ManageStandardPermissionsFragmentTest#additionalPermissionSummaryGetUpdateWhenUserGetsUninstalled"
+ }
+ ]
+ },
+ {
+ "name": "CtsPermissionUiTestCases[com.google.android.permission.apex]"
+ }
+ ],
"imports": [
{
"path": "vendor/xts/gts-tests/hostsidetests/permissioncontroller"
diff --git a/PermissionController/WEAR_OWNERS b/PermissionController/WEAR_OWNERS
new file mode 100644
index 000000000..da9486f1c
--- /dev/null
+++ b/PermissionController/WEAR_OWNERS
@@ -0,0 +1,3 @@
+adsule@google.com
+sadrul@google.com
+youngjoonyang@google.com
diff --git a/PermissionController/iconloaderlib/Android.bp b/PermissionController/iconloaderlib/Android.bp
index 2bc3de435..2a886a276 100644
--- a/PermissionController/iconloaderlib/Android.bp
+++ b/PermissionController/iconloaderlib/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/PermissionController/jarjar-rules.txt b/PermissionController/jarjar-rules.txt
new file mode 100644
index 000000000..5e70dfbbd
--- /dev/null
+++ b/PermissionController/jarjar-rules.txt
@@ -0,0 +1,12 @@
+rule android.content.pm.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.content.pm.Flags com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.permission.flags.Flags com.android.permissioncontroller.jarjar.@0
+rule android.os.*FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.os.FeatureFlags* com.android.permissioncontroller.jarjar.@0
+rule android.os.FeatureFlags com.android.permissioncontroller.jarjar.@0
+rule android.os.Flags com.android.permissioncontroller.jarjar.@0
diff --git a/PermissionController/lint-baseline.xml b/PermissionController/lint-baseline.xml
index 05a307234..be77a0d18 100644
--- a/PermissionController/lint-baseline.xml
+++ b/PermissionController/lint-baseline.xml
@@ -1,147 +1,213 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.app.AppOpsManager.HistoricalOp#getDiscreteAccessAt`"
- errorLine1=" val attributedOpEntry: AttributedOpEntry = it.getDiscreteAccessAt(i)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Class requires API level 34 (current min is 31): `android.app.AppOpsManager.OnOpNotedListener`"
+ errorLine1=" AppOpsManager.OnOpNotedListener,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt"
- line="191"
- column="67"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt"
+ line="46"
+ column="5"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.app.AppOpsManager.HistoricalOp#getDiscreteAccessAt`"
- errorLine1=" val opEntry: AttributedOpEntry = it.getDiscreteAccessAt(i)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getIconId`"
+ errorLine1=" .setIcon(args.getIconId())"
+ errorLine2=" ~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt"
- line="156"
- column="57"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="504"
+ column="31"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.app.AppOpsManager.HistoricalOp#getDiscreteAccessCount`"
- errorLine1=" for (i in 0 until it.discreteAccessCount) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getMessageId`"
+ errorLine1=" .setMessage(args.getMessageId())"
+ errorLine2=" ~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt"
- line="155"
- column="38"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="505"
+ column="34"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.app.AppOpsManager.HistoricalOp#getDiscreteAccessCount`"
- errorLine1=" for (i in 0 until it.discreteAccessCount) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getNegativeButtonTextId`"
+ errorLine1=" .setNegativeButton(args.getNegativeButtonTextId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/v31/LightHistoricalPackageOps.kt"
- line="190"
- column="38"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="509"
+ column="41"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.app.AppOpsManager.HistoricalOpsRequest.Builder#setHistoryFlags`"
- errorLine1=" .setHistoryFlags(HISTORY_FLAG_DISCRETE or HISTORY_FLAG_GET_ATTRIBUTION_CHAINS)"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getPositiveButtonTextId`"
+ errorLine1=" .setPositiveButton(args.getPositiveButtonTextId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt"
- line="101"
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="513"
+ column="41"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `getSetOneTime`"
+ errorLine1=" mViewModel.requestChange(args.getSetOneTime(),"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="515"
+ column="59"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `getChangeRequest`"
+ errorLine1=" args.getChangeRequest(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="518"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `getButtonClicked`"
+ errorLine1=" args.getButtonClicked());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="519"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `getTitleId`"
+ errorLine1=" if (args.getTitleId() != 0) {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="521"
column="18"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.apphibernation.AppHibernationManager#isHibernatingForUser`"
- errorLine1=" if (hibernationManager.isHibernatingForUser(pkg.packageName)) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getTitleId`"
+ errorLine1=" b.setTitle(args.getTitleId());"
+ errorLine2=" ~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt"
- line="56"
- column="44"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionFragment.java"
+ line="522"
+ column="29"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.pm.Attribution#getLabel`"
- errorLine1=" attributions?.forEach { attributionTagToLabel[it.tag] = it.label }"
- errorLine2=" ~~~~~">
+ message="Call requires API level 33 (current min is 30): `getIconId`"
+ errorLine1=" .setIcon(args.getIconId())"
+ errorLine2=" ~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt"
- line="125"
- column="72"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="709"
+ column="31"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.pm.Attribution#getTag`"
- errorLine1=" attributions?.forEach { attributionTagToLabel[it.tag] = it.label }"
- errorLine2=" ~~~">
+ message="Call requires API level 33 (current min is 30): `getMessageId`"
+ errorLine1=" .setMessage(args.getMessageId())"
+ errorLine2=" ~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt"
- line="125"
- column="62"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="710"
+ column="34"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#canAdminGrantSensorsPermissions`"
- errorLine1=" params.getGrantState(), params.canAdminGrantSensorsPermissions())));"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getNegativeButtonTextId`"
+ errorLine1=" .setNegativeButton(args.getNegativeButtonTextId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
- line="517"
- column="48"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="714"
+ column="41"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#getGrantState`"
- errorLine1=" params.getGrantState(), params.canAdminGrantSensorsPermissions())));"
- errorLine2=" ~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getPositiveButtonTextId`"
+ errorLine1=" .setPositiveButton(args.getPositiveButtonTextId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
- line="517"
- column="24"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="718"
+ column="41"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#getGranteePackageName`"
- errorLine1=" callerPackageName, params.getGranteePackageName(), params.getPermission(),"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getSetOneTime`"
+ errorLine1=" mViewModel.requestChange(args.getSetOneTime(),"
+ errorLine2=" ~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
- line="516"
- column="43"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="720"
+ column="59"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#getPermission`"
- errorLine1=" callerPackageName, params.getGranteePackageName(), params.getPermission(),"
- errorLine2=" ~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getButtonClicked`"
+ errorLine1=" args.getChangeRequest(), args.getButtonClicked());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
- line="516"
- column="75"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="722"
+ column="67"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.hardware.SensorPrivacyManager#addSensorPrivacyListener`"
- errorLine1=" mSensorPrivacyManager.addSensorPrivacyListener(mPrivacyChangedListener);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getChangeRequest`"
+ errorLine1=" args.getChangeRequest(), args.getButtonClicked());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java"
- line="159"
- column="35"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="722"
+ column="42"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `getTitleId`"
+ errorLine1=" if (args.getTitleId() != 0) {"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="724"
+ column="18"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `getTitleId`"
+ errorLine1=" b.setTitle(args.getTitleId());"
+ errorLine2=" ~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java"
+ line="725"
+ column="29"/>
</issue>
<issue
@@ -150,8 +216,8 @@
errorLine1=" mSensorPrivacyManager.addSensorPrivacyListener(mPrivacyChangedListener);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionAppsFragment.java"
- line="114"
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AppPermissionsFragment.java"
+ line="159"
column="35"/>
</issue>
@@ -168,123 +234,134 @@
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.hardware.SensorPrivacyManager#removeSensorPrivacyListener`"
- errorLine1=" mSensorPrivacyManager.removeSensorPrivacyListener(mPrivacyChangedListener);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getIconId`"
+ errorLine1=" .setIcon(args.getIconId())"
+ errorLine2=" ~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionAppsFragment.java"
- line="365"
- column="35"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="463"
+ column="31"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getIssues`"
- errorLine1=" ) : this(safetyCenterData.status, hasIssues = safetyCenterData.issues.size &gt; 0)"
- errorLine2=" ~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getMessageId`"
+ errorLine1=" .setMessage(args.getMessageId())"
+ errorLine2=" ~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt"
- line="18"
- column="68"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="464"
+ column="34"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getIssues`"
- errorLine1=" issues"
- errorLine2=" ~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getNegativeButtonTextId`"
+ errorLine1=" .setNegativeButton(args.getNegativeButtonTextId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="309"
- column="5"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="468"
+ column="41"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getIssues`"
- errorLine1="private fun SafetyCenterData.buildIssueIdSet(): Set&lt;IssueId&gt; = issues.map { it.id }.toSet()"
- errorLine2=" ~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getPositiveButtonTextId`"
+ errorLine1=" .setPositiveButton(args.getPositiveButtonTextId(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="323"
- column="64"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="472"
+ column="41"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getStatus`"
- errorLine1=" ) : this(safetyCenterData.status, hasIssues = safetyCenterData.issues.size &gt; 0)"
- errorLine2=" ~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getSetOneTime`"
+ errorLine1=" mViewModel.requestChange(args.getSetOneTime(),"
+ errorLine2=" ~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt"
- line="18"
- column="31"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="474"
+ column="59"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getStatus`"
- errorLine1=" status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS"
- errorLine2=" ~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getButtonClicked`"
+ errorLine1=" args.getChangeRequest(), args.getButtonClicked());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="321"
- column="5"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="476"
+ column="67"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue#getActions`"
- errorLine1=" issue.actions"
- errorLine2=" ~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `getChangeRequest`"
+ errorLine1=" args.getChangeRequest(), args.getButtonClicked());"
+ errorLine2=" ~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="311"
- column="19"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="476"
+ column="42"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue#getId`"
- errorLine1=" .map { issue.id to it.id }"
- errorLine2=" ~~">
+ message="Call requires API level 33 (current min is 30): `getTitleId`"
+ errorLine1=" if (args.getTitleId() != 0) {"
+ errorLine2=" ~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="315"
- column="30"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="478"
+ column="18"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue#getId`"
- errorLine1="private fun SafetyCenterData.buildIssueIdSet(): Set&lt;IssueId&gt; = issues.map { it.id }.toSet()"
- errorLine2=" ~~">
+ message="Call requires API level 33 (current min is 30): `getTitleId`"
+ errorLine1=" b.setTitle(args.getTitleId());"
+ errorLine2=" ~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="323"
- column="80"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java"
+ line="479"
+ column="29"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue.Action#getId`"
- errorLine1=" .map { issue.id to it.id }"
- errorLine2=" ~~">
+ message="Call requires API level 34 (current min is 30): `android.health.connect.HealthConnectManager#isHealthPermission`"
+ errorLine1=" .filter { permission -&gt; isHealthPermission(activity, permission) }"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="315"
- column="39"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt"
+ line="1059"
+ column="45"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue.Action#getSuccessMessage`"
- errorLine1=" .filter { it.isInFlight &amp;&amp; !it.successMessage.isNullOrEmpty() }"
- errorLine2=" ~~~~~~~~~~~~~~">
+ message="Class requires API level 31 (current min is 30): `android.apphibernation.AppHibernationManager`"
+ errorLine1=" userContext.getSystemService(APP_HIBERNATION_SERVICE) as AppHibernationManager"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="314"
- column="48"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt"
+ line="48"
+ column="74"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.apphibernation.AppHibernationManager#isHibernatingForUser`"
+ errorLine1=" if (hibernationManager.isHibernatingForUser(pkg.packageName)) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt"
+ line="51"
+ column="44"/>
</issue>
<issue
@@ -300,266 +377,310 @@
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue.Action#isInFlight`"
- errorLine1=" .filter { it.isInFlight &amp;&amp; !it.successMessage.isNullOrEmpty() }"
- errorLine2=" ~~~~~~~~~~">
+ message="Call requires API level 31 (current min is 30): `android.os.UserManager#isCloneProfile`"
+ errorLine1=" if (userManager.isCloneProfile) {"
+ errorLine2=" ~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="314"
- column="30"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt"
+ line="653"
+ column="29"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#isSafetyCenterEnabled`"
- errorLine1=" if (!safetyCenterManager.isSafetyCenterEnabled()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.app.Activity#startActivityForResultAsUser`"
+ errorLine1=" activity.startActivityForResultAsUser(pickerIntent, requestCode, user)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
- line="96"
- column="34"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt"
+ line="662"
+ column="18"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#isSafetyCenterEnabled`"
- errorLine1=" if (!safetyCenterManager.isSafetyCenterEnabled()) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 34 (current min is 30): `android.health.connect.HealthConnectManager#getHealthPermissions`"
+ errorLine1=" val permissions = HealthConnectManager.getHealthPermissions(context)"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
- line="149"
- column="34"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt"
+ line="1689"
+ column="48"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#isSafetyCenterEnabled`"
- errorLine1=" if (!scManager.isSafetyCenterEnabled) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 31 (current min is 30): `android.content.pm.Attribution#getLabel`"
+ errorLine1=" attributions?.forEach { attributionTagToLabel[it.tag] = it.label }"
+ errorLine2=" ~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt"
- line="48"
- column="24"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt"
+ line="140"
+ column="72"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#refreshSafetySources`"
- errorLine1=" safetyCenterManager.refreshSafetySources(getRefreshReason());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 31 (current min is 30): `android.content.pm.Attribution#getTag`"
+ errorLine1=" attributions?.forEach { attributionTagToLabel[it.tag] = it.label }"
+ errorLine2=" ~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
- line="155"
- column="29"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt"
+ line="140"
+ column="62"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterStatus#getRefreshStatus`"
- errorLine1=" when (status.refreshStatus) {"
- errorLine2=" ~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue#getActions`"
+ errorLine1=" issue.actions"
+ errorLine2=" ~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt"
- line="65"
- column="26"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="288"
+ column="19"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterStatus#getRefreshStatus`"
- errorLine1=" status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS"
- errorLine2=" ~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue.Action#getSuccessMessage`"
+ errorLine1=" .filter { it.isInFlight &amp;&amp; !it.successMessage.isNullOrEmpty() }"
+ errorLine2=" ~~~~~~~~~~~~~~">
<location
file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
- line="321"
- column="12"/>
+ line="291"
+ column="48"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `recordPermissionDecision`"
- errorLine1=" PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue.Action#isInFlight`"
+ errorLine1=" .filter { it.isInFlight &amp;&amp; !it.successMessage.isNullOrEmpty() }"
+ errorLine2=" ~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt"
- line="1147"
- column="39"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="291"
+ column="30"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 34 (current min is 30): `android.content.pm.PackageManager#getAppMetadata`"
- errorLine1=" app.packageManager.getAppMetadata(packageName)"
- errorLine2=" ~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue#getId`"
+ errorLine1=" .map { issue.id to it.id }"
+ errorLine2=" ~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/SafetyLabelInfoLiveData.kt"
- line="116"
- column="32"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="292"
+ column="30"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 34 (current min is 30): `android.health.connect.HealthConnectManager#getHealthPermissions`"
- errorLine1=" val permissions = HealthConnectManager.getHealthPermissions(context)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue.Action#getId`"
+ errorLine1=" .map { issue.id to it.id }"
+ errorLine2=" ~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt"
- line="1465"
- column="48"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="292"
+ column="39"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 34 (current min is 30): `android.health.connect.HealthConnectManager#isHealthPermission`"
- errorLine1=" isHealthPermission(activity, permission)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getStatus`"
+ errorLine1=" status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS"
+ errorLine2=" ~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt"
- line="1268"
- column="17"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="298"
+ column="5"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 34 (current min is 33): `getParentGroupId`"
- errorLine1=" String groupId = getParentGroupId(preferenceKey);"
- errorLine2=" ~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterStatus#getRefreshStatus`"
+ errorLine1=" status.refreshStatus == SafetyCenterStatus.REFRESH_STATUS_FULL_RESCAN_IN_PROGRESS"
+ errorLine2=" ~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java"
- line="89"
- column="30"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="298"
+ column="12"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 34 (current min is 33): `openRelevantSubpage`"
- errorLine1=" frag = openRelevantSubpage(groupId);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterIssue#getId`"
+ errorLine1=" allResolvableIssues.map { it.id }.toSet()"
+ errorLine2=" ~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java"
- line="86"
- column="20"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="301"
+ column="34"/>
</issue>
<issue
id="NewApi"
- message="Call requires API level 34 (current min is 33): `openRelevantSubpage`"
- errorLine1=" frag = openRelevantSubpage(groupId);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterData#getIssues`"
+ errorLine1=" issues.asSequence()"
+ errorLine2=" ~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java"
- line="90"
- column="20"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt"
+ line="308"
+ column="13"/>
</issue>
<issue
id="NewApi"
- message="Class requires API level 31 (current min is 30): `android.apphibernation.AppHibernationManager`"
- errorLine1=" userContext.getSystemService(APP_HIBERNATION_SERVICE) as AppHibernationManager"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ message="Field requires API level 33 (current min is 30): `Companion`"
+ errorLine1=" MoreIssuesCardPreference.TAG,"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/HibernatedPackagesLiveData.kt"
- line="53"
- column="74"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt"
+ line="110"
+ column="21"/>
</issue>
<issue
id="NewApi"
- message="Class requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager`"
- errorLine1=" context.getSystemService(SafetyCenterManager.class);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Field requires API level 33 (current min is 30): `getTAG`"
+ errorLine1=" MoreIssuesCardPreference.TAG,"
+ errorLine2=" ~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
- line="84"
- column="42"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt"
+ line="110"
+ column="46"/>
</issue>
<issue
id="NewApi"
- message="Class requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager`"
- errorLine1=" SafetyCenterManager safetyCenterManager = this.getSystemService(SafetyCenterManager.class);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.hardware.SensorPrivacyManager#addSensorPrivacyListener`"
+ errorLine1=" mSensorPrivacyManager.addSensorPrivacyListener(mPrivacyChangedListener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
- line="144"
- column="73"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionAppsFragment.java"
+ line="114"
+ column="35"/>
</issue>
<issue
id="NewApi"
- message="Class requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager`"
- errorLine1=" val scManager = getSystemService(SafetyCenterManager::class.java)!!"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.hardware.SensorPrivacyManager#removeSensorPrivacyListener`"
+ errorLine1=" mSensorPrivacyManager.removeSensorPrivacyListener(mPrivacyChangedListener);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt"
- line="41"
- column="42"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionAppsFragment.java"
+ line="365"
+ column="35"/>
</issue>
<issue
id="NewApi"
- message="Class requires API level 34 (current min is 30): `android.app.AppOpsManager.OnOpNotedListener`"
- errorLine1=" AppOpsManager.OnOpNotedListener,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 31 (current min is 30): `SensorStatusLiveData`"
+ errorLine1=" lazy(LazyThreadSafetyMode.NONE) { SensorStatusLiveData() }"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightHistoricalPackageOpsLiveData.kt"
- line="45"
- column="5"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/ui/model/PermissionAppsViewModel.kt"
+ line="100"
+ column="43"/>
</issue>
<issue
id="NewApi"
- message="Class requires API level 34 (current min is 30): `android.app.AppOpsManager.OnOpNotedListener`"
- errorLine1=" AppOpsManager.OnOpNotedListener,"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#getGranteePackageName`"
+ errorLine1=" callerPackageName, params.getGranteePackageName(), params.getPermission(),"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt"
- line="38"
- column="5"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
+ line="521"
+ column="43"/>
</issue>
<issue
id="NewApi"
- message="Field requires API level 33 (current min is 30): `getTAG`"
- errorLine1=" MoreIssuesCardPreference.TAG,"
- errorLine2=" ~~~">
+ message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#getPermission`"
+ errorLine1=" callerPackageName, params.getGranteePackageName(), params.getPermission(),"
+ errorLine2=" ~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/MoreIssuesCardAnimator.kt"
- line="107"
- column="46"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
+ line="521"
+ column="75"/>
</issue>
<issue
id="NewApi"
- message="Method reference requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterStatus::severityLevel`"
- errorLine1=" val severityLevel: Int by status::severityLevel"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#canAdminGrantSensorsPermissions`"
+ errorLine1=" params.getGrantState(), params.canAdminGrantSensorsPermissions())));"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt"
- line="30"
- column="31"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
+ line="522"
+ column="48"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 31 (current min is 30): `android.permission.AdminPermissionControlParams#getGrantState`"
+ errorLine1=" params.getGrantState(), params.canAdminGrantSensorsPermissions())));"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java"
+ line="522"
+ column="24"/>
</issue>
<issue
id="NewApi"
- message="Method reference requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterStatus::summary`"
- errorLine1=" val originalSummary: CharSequence by status::summary"
- errorLine2=" ~~~~~~~~~~~~~~~">
+ message="Class requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager`"
+ errorLine1=" context.getSystemService(SafetyCenterManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt"
- line="29"
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
+ line="85"
column="42"/>
</issue>
<issue
id="NewApi"
- message="Method reference requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterStatus::title`"
- errorLine1=" val title: CharSequence by status::title"
- errorLine2=" ~~~~~~~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#isSafetyCenterEnabled`"
+ errorLine1=" if (!safetyCenterManager.isSafetyCenterEnabled()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
+ line="97"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Class requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager`"
+ errorLine1=" SafetyCenterManager safetyCenterManager = this.getSystemService(SafetyCenterManager.class);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
+ line="141"
+ column="73"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#isSafetyCenterEnabled`"
+ errorLine1=" if (!safetyCenterManager.isSafetyCenterEnabled()) {"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/StatusUiData.kt"
- line="28"
- column="32"/>
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
+ line="146"
+ column="34"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 33 (current min is 30): `android.safetycenter.SafetyCenterManager#refreshSafetySources`"
+ errorLine1=" safetyCenterManager.refreshSafetySources(getRefreshReason());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java"
+ line="152"
+ column="29"/>
</issue>
</issues> \ No newline at end of file
diff --git a/PermissionController/proguard.flags b/PermissionController/proguard.flags
index 13590aa39..292e3e4f4 100644
--- a/PermissionController/proguard.flags
+++ b/PermissionController/proguard.flags
@@ -31,3 +31,13 @@
*** set*(***);
*** has*();
}
+
+# Strip verbose logs.
+-assumenosideeffects class android.util.Log {
+ static *** v(...);
+ static *** isLoggable(...);
+}
+-assumenosideeffects class android.util.Slog {
+ static *** v(...);
+}
+-maximumremovedandroidloglevel 2
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/drawable/ic_lock_closed.xml b/PermissionController/res/drawable/ic_lock_closed.xml
new file mode 100644
index 000000000..e61a2d4a5
--- /dev/null
+++ b/PermissionController/res/drawable/ic_lock_closed.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+-->
+<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="@android:color/white"
+ android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,8h-1.5V5.5C16.5,3.01 14.49,1 12,1S7.5,3.01 7.5,5.5V8H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9.5,5.5C9.5,4.12 10.62,3 12,3c1.38,0 2.5,1.12 2.5,2.5V8h-5V5.5zM18,20H6V10h1.5h9H18V20z"/>
+</vector>
diff --git a/PermissionController/res/drawable/ic_more_horizontal.xml b/PermissionController/res/drawable/ic_more_horizontal.xml
new file mode 100644
index 000000000..c770e08ef
--- /dev/null
+++ b/PermissionController/res/drawable/ic_more_horizontal.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
+</vector>
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..67ac627fc 100644
--- a/PermissionController/res/layout/app_permission.xml
+++ b/PermissionController/res/layout/app_permission.xml
@@ -83,53 +83,84 @@
android:id="@+id/permission_message"
style="@style/AppPermissionMessage" />
- <RadioGroup
- android:id="@+id/radiogroup"
- android:animateLayoutChanges="true"
+ <FrameLayout
+ android:id="@+id/allow_radio_button_frame"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
+ android:layout_height="match_parent">
<RadioButton
android:id="@+id/allow_radio_button"
android:text="@string/app_permission_button_allow"
+ android:clickable="false"
+ android:focusable="false"
style="@style/AppPermissionRadioButton" />
+ </FrameLayout>
- <RadioButton
- android:id="@+id/allow_always_radio_button"
- android:text="@string/app_permission_button_allow_always"
- style="@style/AppPermissionRadioButton" />
-
- <RadioButton
- android:id="@+id/allow_foreground_only_radio_button"
- 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" />
-
- <RadioButton
- android:id="@+id/ask_one_time_radio_button"
- android:text="@string/app_permission_button_ask"
- style="@style/AppPermissionRadioButton" />
+ <RadioButton
+ android:id="@+id/allow_always_radio_button"
+ android:text="@string/app_permission_button_allow_always"
+ style="@style/AppPermissionRadioButton" />
- <RadioButton
- android:id="@+id/ask_radio_button"
- android:text="@string/app_permission_button_ask"
- style="@style/AppPermissionRadioButton" />
+ <RadioButton
+ android:id="@+id/allow_foreground_only_radio_button"
+ android:text="@string/app_permission_button_allow_foreground"
+ style="@style/AppPermissionRadioButton" />
- <RadioButton
- android:id="@+id/deny_radio_button"
- android:text="@string/app_permission_button_deny"
- 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/deny_foreground_radio_button"
- android:text="@string/app_permission_button_deny"
- style="@style/AppPermissionRadioButton" />
+ android:id="@+id/select_radio_button"
+ android:text="@string/app_permission_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_marginStart="16dp"
+ android:layout_marginEnd="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"
+ android:background="@null"
+ android:src="@drawable/ic_edit"/>
+ </RelativeLayout>
+
+ <RadioButton
+ android:id="@+id/ask_one_time_radio_button"
+ android:text="@string/app_permission_button_ask"
+ style="@style/AppPermissionRadioButton" />
+
+ <RadioButton
+ android:id="@+id/ask_radio_button"
+ android:text="@string/app_permission_button_ask"
+ style="@style/AppPermissionRadioButton" />
+
+ <RadioButton
+ android:id="@+id/deny_radio_button"
+ android:text="@string/app_permission_button_deny"
+ style="@style/AppPermissionRadioButton" />
+
+ <RadioButton
+ android:id="@+id/deny_foreground_radio_button"
+ android:text="@string/app_permission_button_deny"
+ style="@style/AppPermissionRadioButton" />
- </RadioGroup>
<LinearLayout
android:layout_width="match_parent"
diff --git a/PermissionController/res/layout/car_warning_banner_preference_card.xml b/PermissionController/res/layout/car_warning_banner_preference_card.xml
new file mode 100644
index 000000000..af39fa1c2
--- /dev/null
+++ b/PermissionController/res/layout/car_warning_banner_preference_card.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/WarningBannerMainContainer" >
+
+ <androidx.cardview.widget.CardView
+ style="@style/AutoWarningBannerCardView" >
+
+ <RelativeLayout
+ style="@style/WarningBannerDimensions" >
+
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ style="@style/AutoWarningBannerIcon" />
+
+ <TextView android:id="@android:id/title"
+ android:layout_below="@android:id/icon"
+ style="@style/AutoWarningBannerTitle" />
+
+ <TextView android:id="@android:id/summary"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ style="@style/AutoWarningBannerSummary" />
+
+ </RelativeLayout>
+
+ </androidx.cardview.widget.CardView>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@android:id/widget_frame"
+ style="@style/WarningBannerWidgetFrame" />
+
+</LinearLayout>
diff --git a/PermissionController/res/layout/enhanced_confirmation_dialog.xml b/PermissionController/res/layout/enhanced_confirmation_dialog.xml
new file mode 100644
index 000000000..dde2e3f69
--- /dev/null
+++ b/PermissionController/res/layout/enhanced_confirmation_dialog.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/enhanced_confirmation_dialog"
+ style="@style/EnhancedConfirmationDialog">
+ <LinearLayout
+ android:id="@+id/enhanced_confirmation_dialog_header"
+ style="@style/EnhancedConfirmationDialogHeader">
+ <ImageView
+ android:id="@+id/enhanced_confirmation_dialog_icon"
+ style="@style/EnhancedConfirmationDialogIcon" />
+ <TextView
+ android:id="@+id/enhanced_confirmation_dialog_title"
+ android:text="@string/enhanced_confirmation_dialog_title"
+ style="@style/EnhancedConfirmationDialogTitle" />
+ </LinearLayout>
+
+ <ScrollView
+ android:id="@+id/enhanced_confirmation_dialog_scrollview"
+ style="@style/EnhancedConfirmationDialogScrollView">
+ <LinearLayout
+ android:id="@+id/enhanced_confirmation_dialog_body"
+ style="@style/EnhancedConfirmationDialogBody">
+ <TextView
+ android:id="@+id/enhanced_confirmation_dialog_desc"
+ android:text="@string/enhanced_confirmation_dialog_desc"
+ style="@style/EnhancedConfirmationDialogDesc" />
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
diff --git a/PermissionController/res/layout/grant_permissions.xml b/PermissionController/res/layout/grant_permissions.xml
index 9becddb7f..dead3ccf8 100644
--- a/PermissionController/res/layout/grant_permissions.xml
+++ b/PermissionController/res/layout/grant_permissions.xml
@@ -37,6 +37,7 @@
android:id="@+id/grant_dialog"
android:theme="@style/Theme.PermissionGrantDialog"
android:importantForAccessibility="no"
+ android:focusable="false"
style="@style/PermissionGrantDialog">
<LinearLayout
@@ -143,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/PermissionGrantButtonAllowSelected" />
<com.android.permissioncontroller.permission.ui.widget.SecureButton
@@ -184,7 +185,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..4fbae3d11 100644
--- a/PermissionController/res/layout/grant_permissions_material3.xml
+++ b/PermissionController/res/layout/grant_permissions_material3.xml
@@ -38,6 +38,7 @@
android:id="@+id/grant_dialog"
android:theme="@style/Theme.PermissionGrantDialog"
android:importantForAccessibility="no"
+ android:focusable="false"
style="@style/PermissionGrantDialogMaterial3">
<LinearLayout
@@ -144,7 +145,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/layout/request_role_item.xml b/PermissionController/res/layout/request_role_item.xml
index d7d142289..39e458aa2 100644
--- a/PermissionController/res/layout/request_role_item.xml
+++ b/PermissionController/res/layout/request_role_item.xml
@@ -24,28 +24,23 @@
<ImageView
android:id="@+id/icon"
- android:duplicateParentState="true"
style="@style/RequestRoleItemIcon" />
<LinearLayout
android:id="@+id/title_and_subtitle"
- android:duplicateParentState="true"
style="@style/RequestRoleItemTitleLayout">
<TextView
android:id="@+id/title"
- android:duplicateParentState="true"
style="@style/RequestRoleItemTitleText" />
<TextView
android:id="@+id/subtitle"
- android:duplicateParentState="true"
style="@style/RequestRoleItemSubtitleText" />
</LinearLayout>
<RadioButton
android:clickable="false"
- android:duplicateParentState="true"
android:focusable="false"
style="@style/RequestRoleItemRadioButton" />
</com.android.permissioncontroller.role.ui.CheckableLinearLayout>
diff --git a/PermissionController/res/navigation-watch/nav_graph.xml b/PermissionController/res/navigation-watch/nav_graph.xml
new file mode 100644
index 000000000..7af2c3e39
--- /dev/null
+++ b/PermissionController/res/navigation-watch/nav_graph.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.
+ -->
+
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/nav_graph"
+ app:startDestination="@id/manage_standard">
+
+ <!-- For explanation of the navigation component, and this graph, see
+ https://developer.android.com/guide/navigation -->
+
+ <fragment
+ android:id="@+id/manage_standard"
+ android:name="com.android.permissioncontroller.permission.ui.wear.WearManageStandardPermissionsFragment"
+ android:label="ManageStandard">
+
+ <action
+ android:id="@+id/standard_to_custom"
+ app:destination="@id/manage_custom"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popEnterAnim="@anim/activity_close_enter"
+ app:popExitAnim="@anim/activity_close_exit"/>
+
+ <action
+ android:id="@+id/manage_to_perm_apps"
+ app:destination="@id/permission_apps"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popEnterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"/>
+
+ <action
+ android:id="@+id/manage_to_auto_revoke"
+ app:destination="@id/auto_revoke"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popEnterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"/>
+
+ </fragment>
+
+ <fragment
+ android:id="@+id/manage_custom"
+ android:name="com.android.permissioncontroller.permission.ui.wear.WearManageCustomPermissionsFragment"
+ android:label="ManageCustom">
+
+ <action
+ android:id="@+id/manage_to_perm_apps"
+ app:destination="@id/permission_apps"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"
+ app:popEnterAnim="@anim/activity_open_enter"/>
+
+ </fragment>
+
+ <fragment
+ android:id="@+id/auto_revoke"
+ android:name="com.android.permissioncontroller.permission.ui.wear.WearUnusedAppsFragment"
+ android:label="AutoRevoke">
+
+ <action
+ android:id="@+id/auto_revoke_to_app_perms"
+ app:destination="@id/app_permission_groups"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"
+ app:popEnterAnim="@anim/activity_open_enter"/>
+
+ </fragment>
+
+ <fragment
+ android:id="@+id/app_permission_groups"
+ android:name="com.android.permissioncontroller.permission.ui.wear.WearAppPermissionGroupsFragment"
+ android:label="AppPermissionGroups">
+
+ <action
+ android:id="@+id/perm_groups_to_app"
+ app:destination="@id/app_permission"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"
+ app:popEnterAnim="@anim/activity_open_enter"/>
+
+ <action
+ android:id="@+id/perm_groups_to_all_perms"
+ app:destination="@id/all_app_permissions"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"
+ app:popEnterAnim="@anim/activity_open_enter"/>
+
+ <action
+ android:id="@+id/perm_groups_to_custom"
+ app:destination="@id/custom_app_permission_groups"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"
+ app:popEnterAnim="@anim/activity_open_enter"/>
+ </fragment>
+
+ <fragment
+ android:id="@+id/app_permission"
+ android:name="com.android.permissioncontroller.permission.ui.wear.WearAppPermissionFragment"
+ android:label="AppPermission" />
+
+ <fragment
+ android:id="@+id/permission_apps"
+ android:name="com.android.permissioncontroller.permission.ui.wear.WearPermissionAppsFragment"
+ android:label="PermissionApps">
+
+ <action
+ android:id="@+id/perm_apps_to_app"
+ app:destination="@id/app_permission"
+ app:enterAnim="@anim/activity_open_enter"
+ app:popExitAnim="@anim/activity_close_exit"
+ app:popEnterAnim="@anim/activity_open_enter"/>
+ </fragment>
+</navigation>
diff --git a/PermissionController/res/values-af-v34/strings.xml b/PermissionController/res/values-af-v34/strings.xml
index db69a7032..9a423aa78 100644
--- a/PermissionController/res/values-af-v34/strings.xml
+++ b/PermissionController/res/values-af-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Bestuur apptoegang tot gesondheidsdata"</string>
<string name="location_settings" msgid="8863940440881290182">"Liggingtoegang"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"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="6846532794702613851">"Vir apps en dienste"</string>
</resources>
diff --git a/PermissionController/res/values-af-watch/strings.xml b/PermissionController/res/values-af-watch/strings.xml
index 3386aa514..c6cfd6038 100644
--- a/PermissionController/res/values-af-watch/strings.xml
+++ b/PermissionController/res/values-af-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Onveranderbaar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ja"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Kanselleer"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Altyd"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Terwyl app gebruik word"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Altyd"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Terwyl app gebruik word"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Altyd"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Terwyl app gebruik word"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Altyd"</string>
</resources>
diff --git a/PermissionController/res/values-af/strings.xml b/PermissionController/res/values-af/strings.xml
index 977651196..92c854954 100644
--- a/PermissionController/res/values-af/strings.xml
+++ b/PermissionController/res/values-af/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"toestemmings"</string>
<string name="cancel" msgid="8943320028373963831">"Kanselleer"</string>
<string name="back" msgid="6249950659061523680">"Terug"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Maak toe"</string>
<string name="available" msgid="6007778121920339498">"Beskikbaar"</string>
<string name="blocked" msgid="9195547604866033708">"Geblokkeer"</string>
<string name="on" msgid="280241003226755921">"Aan"</string>
@@ -29,11 +30,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 +52,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 +61,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 +72,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 +91,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Alle toestemmings"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ander programvermoëns"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Toestemmingsversoek"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Installeer- en deïnstalleerhandelinge word nie in Wear gesteun nie."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Kies waartoe &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang mag kry"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is opgedateer. Kies waartoe hierdie program toegang mag kry."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Kanselleer"</string>
@@ -163,7 +164,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>
@@ -191,20 +192,24 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Laat altyd alles toe"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Vra elke keer"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Moenie toelaat nie"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Laat beperkte toegang toe"</string>
<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_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g>toegang vir hierdie app op <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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_label_v3" msgid="693340578642156657">"Bestuur app indien ongebruik"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Verwyder toestemmings, vee tydelike lêers uit, en stop kennisgewings"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Verwyder toestemmings, vee tydelike lêers uit, stop kennisgewings en argiveer die app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Om jou data te beskerm, sal toestemmings vir hierdie program verwyder word as die program \'n paar maande nie gebruik word nie."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Om jou data te beskerm, sal die volgende toestemmings verwyder word as dit vir \'n paar maande nie gebruik word nie: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Om jou data te beskerm, is toestemmings verwyder van programme wat jy \'n paar maande gelede laas gebruik het."</string>
@@ -218,10 +223,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 +237,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Toegelaat om alle lêers te bestuur"</string>
<string name="ask_header" msgid="2633816846459944376">"Vra elke keer"</string>
<string name="denied_header" msgid="903209608358177654">"Nie toegelaat nie"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> op <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Sien meer programme wat toegang tot alle lêers het"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dae}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# uur}other{# uur}}"</string>
@@ -302,7 +308,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 +317,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 +353,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 +379,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 +390,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notasapp"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps wat jou toelaat om notas op jou toestel te maak"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Verstekbeursie-app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet-app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Beursie-apps kan jou krediet- en lojaliteitskaarte, motorsleutels en ander goed stoor om met verskeie vorme van transaksies te help."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Stel <xliff:g id="APP_NAME">%1$s</xliff:g> as jou verstekbeursie-app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Geen toestemmings is nodig nie"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Huidige verstek"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Moenie weer vra nie"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Stel as verstek"</string>
@@ -418,23 +429,24 @@
<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_apps_for_private_profile" msgid="2022024112144880785">"Verstek vir privaat ruimte"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Wys assistent-aktiveringbespeuring"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Wys ikoon in statusbalk wanneer mikrofoon gebruik word om stemassistent te aktiveer"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foto\'s en media op jou toestel?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot foto’s en media op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou kontakte?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou kontakte op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot hierdie toestel se ligging?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; se ligging?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Die program sal net toegang tot die ligging hê terwyl jy die program gebruik"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot hierdie toestel se ligging?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; se ligging?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Hierdie program wil dalk die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Verander liggingtoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Verander liggingtoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Hierdie program wil die hele tyd toegang tot jou ligging hê, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om toestelle in die omtrek te soek, aan hulle te koppel en hul relatiewe posisie te bepaal?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestelle in die omtrek op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; soek, aan hulle koppel, en hul relatiewe posisie bepaal?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om toestelle in die omtrek te soek, aan hulle te koppel en hul relatiewe posisie te bepaal? "<annotation id="link">"Laat toe in Instellings."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Verander <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> se liggingtoegang van benaderd na presies?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Verander <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> se liggingtoegang op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; van benaderd tot presies?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot hierdie toestel se benaderde ligging?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; se benaderde ligging?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Presies"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Benaderd"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou kalender?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou kalendar op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om SMS\'e te stuur en te bekyk?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om SMS-boodskappe te stuur en te bekyk op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Gee vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot foto\'s, media en lêers op jou toestel?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot foto’s, media en lêers op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;foto\'s, video\'s, musiek en oudio&lt;/b&gt; op hierdie toestel?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot &lt;b&gt;foto\'s, video\'s, musiek, oudio en ander lêers&lt;/b&gt; op hierdie toestel?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot musiek en oudio op hierdie toestel?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot musiek en oudio op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot foto\'s en video\'s op hierdie toestel?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot foto’s en video’s op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot meer foto\'s en video\'s op hierdie toestel?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot meer foto’s en video’s op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om oudio op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Die program sal net kan oudio opneem terwyl jy die program gebruik"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om oudio op te neem?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om oudio op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Hierdie program wil dalk die hele tyd oudio opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Verander mikrofoontoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Verander mikrofoontoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Hierdie program wil die hele tyd oudio opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou fisieke aktiwiteit?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou fisieke aktiwiteit op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foto\'s te neem en video\'s op te neem?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om foto’s te neem en video’s op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Die program sal net kan foto\'s neem en video\'s opneem terwyl jy die program gebruik"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foto\'s te neem en video\'s op te neem?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om foto’s te neem en video’s op te neem op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Hierdie program wil dalk die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Verander kameratoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Verander kameratoegang vir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Hierdie program wil die hele tyd foto\'s neem en video\'s opneem, selfs wanneer jy nie die program gebruik nie. "<annotation id="link">"Laat toe in instellings."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foonoproeprekords?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot jou foon se oproeprekords op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om foonoproepe te maak en te bestuur?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om foonoproepe te maak en te bestuur op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot sensordata oor jou lewenstekens op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang tot die sensordata oor jou lewenstekens op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501"><annotation id="link">"Gaan na instellings"</annotation>" om altyd vir hierdie program toegang tot liggaamsensordata te gee, selfs wanneer jy nie die program gebruik nie."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; steeds toegang tot liggaamsensordata terwyl die program gebruik word?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; steeds toegang tot liggaamsensordata op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; terwyl die app gebruik word?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Laat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toe om vir jou kennisgewings te stuur?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Gee &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang om vir jou kennisgewings te stuur op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Beheerde toestemmings"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> het liggingtoegang"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Jou organisasie gee <xliff:g id="APP_NAME">%1$s</xliff:g> toegang tot jou ligging"</string>
@@ -512,14 +551,22 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Geen"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Afgelope\n24 uur"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Afgelope\n7 dae"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> persent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie program se toestemminggebruik word nie op die statusbalk of jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> word deur Android beskerm. Hierdie program se toestemminggebruik word nie op jou privaatheidkontroleskerm gewys nie omdat jou data op hierdie toestel verwerk word."</string>
<string name="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="automotive_blocked_camera_title" msgid="6142362431548829416">"Kameratoegang is af"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofoontoegang is af"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Liggingtoegang is af"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Vir inligtingvermaakapps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Vir vereiste apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Hierdie app is vereis"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Hierdie app word deur jou motor se vervaardiger vereis"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sekuriteit en privaatheid"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skandeer toestel"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Maak toe"</string>
@@ -581,7 +628,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 +658,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Datadelingopdaterings"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Sommige apps het die manier waarop hulle jou liggingdata kan deel, verander"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Instellings"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Ingegaan <xliff:g id="TIME_DATE">%1$s</xliff:g>."</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Gister ingegaan <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Ingegaan <xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Jou eenmalige wagwoord is 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Beperkte instelling"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Hierdie instelling is vir jou veiligheid tans onbeskikbaar."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Toegang tot <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> is geweier vir die app"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Die app het toegang tot toestemming vir sensitiewe inligting versoek wat jou persoonlike en finansiële inligting in gevaar kan stel.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Dit is moontlik dat die app nie reg sal werk sonder hierdie beperkte toestemming nie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kry meer inligting oor hoe om toegang toe te laat&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Toegang is geweier vir app om die verstek <xliff:g id="ROLE_NAME">%1$s</xliff:g> te wees"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Die app het toegang tot toestemming vir sensitiewe inligting versoek wat jou persoonlike en finansiële inligting in gevaar kan stel.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Dit is moontlik dat die app nie reg sal werk sonder hierdie beperkte toestemming nie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kry meer inligting oor hoe om toegang toe te laat&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Toegang is geweier vir die app"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Toegang tot hierdie toestemming kan jou persoonlike en finansiële inligting in gevaar stel.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Dit is moontlik dat die app nie reg sal werk sonder hierdie beperkte toestemming nie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kry meer inligting oor hoe om toegang toe te laat&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Meer inligting"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Toestemmingsversoek is onderdruk"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Hierdie app versoek bykomende toestemmings, maar toestemmings kan nie in ’n stromingsessie verleen word nie. Verleen eers die toestemming op jou foon."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Vir noodoproep of -teksboodskap"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ligging is na nooddienste toe gestuur"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Hierdie app het toegang tot jou toestel se ligging gekry tydens ’n oproep of teksboodskap na ’n noodnommer. Dit kan gebeur selfs al het die app nie liggingtoestemming nie of al is die toestelligging af. "<a href="https://support.google.com/android/answer/9319337">"Meer inligting"</a></string>
</resources>
diff --git a/PermissionController/res/values-am-v34/strings.xml b/PermissionController/res/values-am-v34/strings.xml
index 52f5188ba..9b3edef01 100644
--- a/PermissionController/res/values-am-v34/strings.xml
+++ b/PermissionController/res/values-am-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-am-watch/strings.xml b/PermissionController/res/values-am-watch/strings.xml
index 69a852865..463161920 100644
--- a/PermissionController/res/values-am-watch/strings.xml
+++ b/PermissionController/res/values-am-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ሊለወጥ አይችልም"</string>
<string name="generic_yes" msgid="2489207724988649846">"አዎ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ይቅር"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ሁልጊዜ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"መተግበሪያ እየተጠቀሙ ሳለ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ሁልጊዜ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"መተግበሪያ እየተጠቀሙ ሳለ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ሁልጊዜ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"መተግበሪያ እየተጠቀሙ ሳለ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ሁልጊዜ"</string>
</resources>
diff --git a/PermissionController/res/values-am/strings.xml b/PermissionController/res/values-am/strings.xml
index aa5edc983..0fc1fd049 100644
--- a/PermissionController/res/values-am/strings.xml
+++ b/PermissionController/res/values-am/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ፈቃዶች"</string>
<string name="cancel" msgid="8943320028373963831">"ይቅር"</string>
<string name="back" msgid="6249950659061523680">"ተመለስ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"ዝጋ"</string>
<string name="available" msgid="6007778121920339498">"ይገኛል"</string>
<string name="blocked" msgid="9195547604866033708">"ታግዷል"</string>
<string name="on" msgid="280241003226755921">"በርቷል"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"በWear ላይ የመጫን/ማራገፍ እርምጃዎች አይደገፉም።"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ምን መድረስ እንደሚችል ይምረጡ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ተዘምኗል። ይህ መተግበሪያ ምን መድረስ እንደሚችል ይምረጡ።"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ይቅር"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"የተገደበ መዳረሻ ይፍቀዱ"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ላይ ለዚህ መተግበሪያ የ<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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"ጥቅም ላይ ካልዋለ መተግበሪያን ያስተዳድሩ"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ፈቃዶችን አስወግድ፣ ጊዜያዊ ፋይሎችን ሰርዝ እና ማሳወቂያዎችን አቁም"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ፈቃዶችን ያስወግዱ፣ ጊዜያዊ ፋይሎችን ይሰርዙ፣ ማሳወቂያዎችን ያቁሙ እና መተግበሪያውን በማህደር ያስቀምጡ"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ለእርስዎ ውሂብ ጥበቃ ለማድረግ፣ ለዚህ መተግበሪያ የተሰጡ ፈቃዶች መተግበሪያው ለጥቂት ወራት ጥቅም ላይ ካልዋለ ይህ መተግበሪያ ይወገዳል።"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"የእርስዎን ውሂብ ለመጠበቅ፣ መተግበሪያው ለጥቂት ወራት ጥቅም ላይ ካልዋለ፣ የሚከተሉት ፈቃዶች ይወገዳሉ፦ <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"የእርስዎን ውሂብ ለመጠበቅ ሲባል ለጥቂት ወራት ካልተጠቀሙባቸው መተግበሪያዎች ላይ ፈቃዶች ተወግደዋል።"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ሁሉንም ፋይሎች ማስተዳደር ተፈቀዷል"</string>
<string name="ask_header" msgid="2633816846459944376">"ሁልጊዜ ጠይቅ"</string>
<string name="denied_header" msgid="903209608358177654">"አይፈቀድም"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> በ<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ላይ"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ሁሉንም ፋይሎች መድረስ የሚችሉ ተጨማሪ መተግበሪያዎችን ይመልከቱ"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ቀን}one{# ቀኖች}other{# ቀኖች}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ሰዓት}one{# ሰዓት}other{# ሰዓታት}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ነባሪ የwallet መተግበሪያ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"የwallet መተግበሪያ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"የWallet መተግበሪያዎች የተለያዩ የግብይት ዓይነቶችን ለማገዝ የክሬዲትና የታማኝነት ካርዶችዎን፣ የመኪና ቁልፎችዎን እና ሌሎች ነገሮችን ማከማቸት ይችላሉ።"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> እንደ ነባሪ የWallet መተግበሪያዎ ይቀናበር?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ምንም ፈቃዶች አያስፈልጉም"</string>
<string name="request_role_current_default" msgid="738722892438247184">"አሁን ያለ ነባሪ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ዳግም አትጠይቅ"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"እንደ ነባሪ አዘጋጅ"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"ተጨማሪ ነባሪዎች"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"አገናኞችን በመክፈት ላይ"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ለሥራ ነባሪ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ለግል ቦታ ነባሪ"</string>
<string name="default_app_none" msgid="9084592086808194457">"ምንም"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(የሥርዓት ነባሪ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"መተግበሪያዎች የሉም"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ዕውቂያዎችዎን &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የ&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የ&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ላይ ለ&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የአካባቢ መዳረሻ ይለወጥ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"ይህ መተግበሪያ አካባቢዎን ሁልጊዜ መድረስ ይፈልጋል፣ እርስዎ በማይጠቀሙበት ጊዜም እንኳ። "<annotation id="link">"በቅንብሮች ውስጥ ይፍቀዱ"</annotation>"።"</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; በአቅራቢያ ያሉ የመሣሪያዎች አንጻራዊ አቀማመጥን እንዲፈልግ፣ እንዲገናኝ እና እንዲወስን ይፈቀድለት?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"የ<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> የአካባቢ መዳረሻ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የ&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;’s ግምታዊ አካባቢን እንዲደርስ ይፈቀድ?"</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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ቀን መቁጠሪያዎን &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ላይ ለ&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የማይክሮፎን መዳረሻ ይለወጥ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ላይ ለ&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የካሜራ መዳረሻ ይለወጥ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ላይ ስለ መሠረታዊ ምልክቶችዎ የዳሳሽ ውሂቡን እንዲደርስ ይፈቀድ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"መተግበሪያውን በማይጠቀሙበት ጊዜ እንኳን ይህ መተግበሪያ የሰውነት ዳሳሽ ውሂብን ሁልጊዜ እንዲደርስ ለመፍቀድ "<annotation id="link">"ወደ ቅንብሮች ይሂዱ።"</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_device_aware_sensors" msgid="5542771499929819675">"መተግበሪያው ሥራ ላይ እያለ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; የሰውነት ዳሳሽ ውሂብን &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ምንም"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"ባለፉት\n24 ሰዓታት"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"ባለፉት\n7 ቀናት"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> በመቶ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> በAndroid የተጠበቀ ነው። በዚህ መሣሪያ ላይ የእርስዎ ውሂብ ስለተሰናዳ፣ የዚህ መተግበሪያ ፈቃድ አጠቃቀም በሁኔታ አሞሌ ወይም በግላዊነት ዳሽቦርድዎ ላይ አይታይም።"</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"የካሜራ መዳረሻ ጠፍቷል"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"የማይክሮፎን መዳረሻ ጠፍቷል"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"የአካባቢ መዳረሻ ጠፍቷል"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ለአዝናኝ መረጃ መተግበሪያዎች"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ለሚፈለጉ መተግበሪያዎች"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ይህ መተግበሪያ ያስፈልጋል"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ይህ መተግበሪያ በመኪናዎ አምራች ይፈለጋል"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"በ<xliff:g id="TIME_DATE">%1$s</xliff:g> ላይ ተደርስበታል"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ትላንት በ<xliff:g id="TIME_DATE">%1$s</xliff:g> ላይ ተደርስበታል"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"በ<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ላይ ተደርስበታል"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"የአንድ ጊዜ የይለፍ ቃልዎ 132435 ነው"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"የተገደበ ቅንብር"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ለእርስዎ ደህንነት ሲባል ይህ ቅንብር በአሁኑ ጊዜ አይገኝም።"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"መተግበሪያ የ<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> መዳረሻ ተከልክሏል"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"መተግበሪያው የእርስዎን የግል እና የፋይናንስ መረጃ አደጋ ላይ የሚጥል አደገኛ ፈቃድ እንዲደርስ ጠይቋል።<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ያለዚህ የተገደበ ፈቃድ መተግበሪያው በትክክል ላይሰራ ይችላል። &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;እንዴት መዳረሻን እንደሚፈቅዱ ይረዱ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"መተግበሪያ ነባሪ <xliff:g id="ROLE_NAME">%1$s</xliff:g> የመሆን መዳረሻ ተከልክሏል"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"መተግበሪያው የእርስዎን የግል እና የፋይናንስ መረጃ አደጋ ላይ የሚጥል አደገኛ ፈቃድ እንዲደርስ ጠይቋል።<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ያለዚህ የተገደበ ፈቃድ መተግበሪያው በትክክል ላይሰራ ይችላል። &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;እንዴት መዳረሻን እንደሚፈቅዱ ይረዱ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"መተግበሪያ መዳረሻ ተከልክሏል"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"የዚህ ፈቃድ መዳረሻ የእርስዎን የግል እና የፋይናንስ መረጃ አደጋ ላይ ሊጥል ይችላል።<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ያለዚህ የተገደበ ፈቃድ መተግበሪያው በትክክል ላይሰራ ይችላል። &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;እንዴት መዳረሻን እንደሚፈቅዱ ይረዱ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"የበለጠ ለመረዳት"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"እሺ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"የፈቃድ ጥያቄ ታፍኗል"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ይህ መተግበሪያ ተጨማሪ ፈቃዶችን እየጠየቀ ነው፣ ነገር ግን ፈቃዶች በዥረት ክፍለ ጊዜ ውስጥ ሊሰጡ አይችሉም። መጀመሪያ በስልክዎ ላይ ፈቃድ ይስጡ።"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ለአደጋ ጥሪ ወይም ጽሁፍ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"አካባቢ ወደ ድንገተኛ አደጋ አገልግሎቶች ተልኳል"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ይህ መተግበሪያ ወደ የአደጋ ጊዜ ቁጥር ጥሪ በሚደረግበት ወይም ጽሁፍ በሚላክበት ጊዜ የመሣሪያዎን አካባቢ ደርሷል መተግበሪያው የአካባቢ ፈቃድ ሳይኖረው ወይም የመሣሪያው አካባቢ ሲጠፋም እንኳን ይህ ሊከሰት ይችላል። "<a href="https://support.google.com/android/answer/9319337">"የበለጠ ለመረዳት"</a></string>
</resources>
diff --git a/PermissionController/res/values-ar-v34/strings.xml b/PermissionController/res/values-ar-v34/strings.xml
index 5a0d25e63..af7442dbd 100644
--- a/PermissionController/res/values-ar-v34/strings.xml
+++ b/PermissionController/res/values-ar-v34/strings.xml
@@ -22,6 +22,5 @@
<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="location_settings_subtitle" msgid="6846532794702613851">"للتطبيقات والخدمات"</string>
+ <string name="mic_toggle_description" msgid="1504101620086616040">"للتطبيقات والخدمات. إذا كان هذا الخيار غير مفعّل، قد تتم مشاركة بيانات الميكروفون عند الاتصال برقم طوارئ"</string>
</resources>
diff --git a/PermissionController/res/values-ar-watch/strings.xml b/PermissionController/res/values-ar-watch/strings.xml
index c49e30aa6..fe1bb345b 100644
--- a/PermissionController/res/values-ar-watch/strings.xml
+++ b/PermissionController/res/values-ar-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"لا يمكن التغيير"</string>
<string name="generic_yes" msgid="2489207724988649846">"نعم"</string>
<string name="generic_cancel" msgid="2631708607129269698">"إلغاء"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"طوال الوقت"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"أثناء استخدام التطبيق"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"طوال الوقت"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"أثناء استخدام التطبيق"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"طوال الوقت"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"أثناء استخدام التطبيق"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"طوال الوقت"</string>
</resources>
diff --git a/PermissionController/res/values-ar/strings.xml b/PermissionController/res/values-ar/strings.xml
index 7f61bf813..76159b78c 100644
--- a/PermissionController/res/values-ar/strings.xml
+++ b/PermissionController/res/values-ar/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"أذونات"</string>
<string name="cancel" msgid="8943320028373963831">"إلغاء"</string>
<string name="back" msgid="6249950659061523680">"رجوع"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"إغلاق"</string>
<string name="available" msgid="6007778121920339498">"متاح"</string>
<string name="blocked" msgid="9195547604866033708">"محظور"</string>
<string name="on" msgid="280241003226755921">"مفعَّلة"</string>
@@ -29,11 +30,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"‏لا تتوافق إجراءات التثبيت/إلغاء التثبيت مع نظام Android Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"‏اختيار ما تريد السماح لتطبيق &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إليه"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"‏تمّ تحديث &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. وعليك اختيار ما تريد السماح لهذا التطبيق بالوصول إليه."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"إلغاء"</string>
@@ -163,7 +164,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>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"السماح بالوصول المحدود"</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_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_with_device_name" msgid="7193042925656173271">"إذن \"<xliff:g id="PERM">%1$s</xliff:g>\" لهذا التطبيق على \"<xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"إدارة التطبيق في حال عدم استخدامه"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"إزالة الأذونات وحذف الملفات المؤقتة وإيقاف الإشعارات"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"إزالة الأذونات وحذف الملفات المؤقتة وإيقاف الإشعارات وأرشفة التطبيق"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"لحماية بياناتك، ستتم إزالة أذونات هذا التطبيق إذا لم يتم استخدامه لبضعة أشهر."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"لحماية بياناتك، إذا لم يتم استخدام التطبيق لبضعة أشهر، ستتم إزالة الأذونات التالية: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"لحماية بياناتك، تمت إزالة الأذونات من هذه التطبيقات التي لم تستخدمها منذ بضعة أشهر."</string>
@@ -219,7 +224,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 +237,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"التطبيقات المسموح لها بإدارة كل الملفات"</string>
<string name="ask_header" msgid="2633816846459944376">"الطلب في كل مرة"</string>
<string name="denied_header" msgid="903209608358177654">"التطبيقات غير المسموح لها"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"‫\"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>\" على \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"الاطّلاع على تطبيقات أكثر يمكنها الوصول إلى كل الملفات"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{يوم واحد}zero{# يوم}two{يومان}few{# أيام}many{# يومًا}other{# يوم}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{ساعة واحدة}zero{# ساعة}two{ساعتان}few{# ساعات}many{# ساعةً}other{# ساعة}}"</string>
@@ -315,7 +321,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>
@@ -349,7 +355,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"تطبيق المحفظة التلقائي"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"تطبيق المحفظة"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"يمكن أن تخزِّن تطبيقات المحافِظ معلومات بطاقات الائتمان وبطاقات الولاء والمفاتيح الرقمية للسيارات وغيرها من المعلومات للمساعدة في إدارة أنواع المعاملات المختلفة."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"هل تريد ضبط \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" على أنّه تطبيق المحفظة التلقائي؟"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ما مِن أذونات مطلوبة."</string>
<string name="request_role_current_default" msgid="738722892438247184">"التطبيق التلقائي الحالي"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"لا تسألني مرة أخرى."</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ضبط كتطبيق تلقائي"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"المزيد من الإعدادات التلقائية"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"فتح الروابط"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"التطبيقات التلقائية للعمل"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"التطبيقات التلقائية في المساحة الخاصّة"</string>
<string name="default_app_none" msgid="9084592086808194457">"غير محدَّد"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(الإعداد التلقائي للنظام)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ليست هناك تطبيقات."</string>
@@ -435,7 +447,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>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الصور والوسائط على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى جهات اتصالك على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموقع الجغرافي الخاص بـ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموقع الجغرافي الخاص بـ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"‏هل تريد تغيير إذن وصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى الموقع الجغرافي على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالعثور على الأجهزة المجاورة والربط بها وتحديد موقعها النسبي على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"‏هل تريد تغيير إذن وصول \"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\" إلى الموقع الجغرافي ليكون دقيقًا بدلاً من كونه تقريبيًّا على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموقع الجغرافي التقريبي الخاص بـ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى تقويمك على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بإرسال الرسائل القصيرة وعرضها على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الصور والوسائط والملفات على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الموسيقى والملفات الصوتية على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى الصور والفيديوهات على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى مزيد من الصور والفيديوهات على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بتسجيل الصوت على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بتسجيل الصوت على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"‏هل تريد تغيير إذن وصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى الميكروفون على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى بيانات نشاطك البدني على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتقاط الصور وتسجيل الفيديو على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالتقاط الصور وتسجيل الفيديو على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"‏هل تريد تغيير إذن وصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى الكاميرا على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بالوصول إلى سجلّ مكالمات الهاتف على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بإجراء المكالمات الهاتفية وإدارتها على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"‏هل تريد السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى بيانات جهاز الاستشعار المتعلّقة بالمؤشرات الحيوية على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"‏هل تريد السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى بيانات جهاز الاستشعار المتعلّقة بالمؤشرات الحيوية على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;؟"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"للسماح لهذا التطبيق بالوصول دائمًا إلى بيانات أجهزة استشعار الجسم حتى عند عدم استخدامك للتطبيق، "<annotation id="link">"انتقِل إلى الإعدادات"</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_device_aware_sensors" msgid="5542771499929819675">"‏هل تريد مواصلة السماح بوصول &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; إلى بيانات جهاز استشعار الجسم على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"‏هل تريد السماح لـ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; بإرسال إشعارات إليك على &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,14 +551,22 @@
<string name="privdash_label_none" msgid="5991866260360484858">"بلا أذونات"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"آخر\n24 ساعة"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"آخر\n7 أيام"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> في المئة"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"‏تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> محمي بواسطة Android. لأن بياناتك تتم معالجتها على هذا الجهاز، لا يظهر استخدام إذن هذا التطبيق في شريط الحالة أو لوحة بيانات الخصوصية."</string>
<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>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"إذن الوصول إلى الكاميرا غير مفعَّل"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"إذن الوصول إلى الميكروفون غير مفعَّل"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"إذن الوصول إلى الموقع الجغرافي غير مفعَّل"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"لتطبيقات المعلومات والترفيه"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"للتطبيقات المطلوبة"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"يجب استخدام هذا التطبيق"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"تتطلّب الشركة المصنِّعة لسيارتك استخدام هذا التطبيق"</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>
@@ -536,7 +583,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 +627,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,8 +662,27 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"‏آخر استخدام للإذن: ‎<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"‏آخر استخدام للإذن أمس: ‎<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"‏آخر استخدام للإذن في ‎<xliff:g id="TIME_DATE_0">%1$s</xliff:g>: ‏<xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"كلمة المرور الصالحة لمرة واحدة هي 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"الإعداد محظور"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"للحفاظ على أمانك، هذا الإعداد غير متوفِّر حاليًا."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"تم منع التطبيق من الوصول إلى <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏طلب التطبيق أحد أذونات الوصول إلى المعلومات الحساسة، ما قد يعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذا الإذن المقيَّد. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;مزيد من المعلومات حول كيفية منح الأذونات&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"لم يتم السماح للتطبيق بأن يُستخدَم كـ <xliff:g id="ROLE_NAME">%1$s</xliff:g> تلقائيًا"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"‏طلب التطبيق أذونات الوصول إلى المعلومات الحساسة، ما قد يعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذه الأذونات المقيَّدة. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;مزيد من المعلومات حول كيفية منح الأذونات&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"تم منع التطبيق من الوصول"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"‏إذا منحت التطبيق هذا الإذن، يمكن أن تتعرّض معلوماتك الشخصية والمالية للخطر.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>من المحتمل ألا يعمل التطبيق بشكل صحيح إذا لم يحصل على هذا الإذن المقيَّد. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;مزيد من المعلومات حول كيفية منح الأذونات&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"مزيد من المعلومات"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"حسنًا"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"تم إلغاء طلب الحصول على الإذن"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"يطلب هذا التطبيق الحصول على أذونات إضافية، ولكن لا يمكن منح أذونات في جلسة بث. امنح الإذن على هاتفك أولاً."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"مراسلة نصية أو مكالمة طوارئ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"تم إرسال بيانات الموقع الجغرافي إلى خدمات طوارئ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"وصل هذا التطبيق إلى الموقع الجغرافي لجهازك أثناء إجراء اتصال برقم طوارئ أو مراسلته نصيًا. يمكن أن يحدث ذلك حتى إذا لم يحصل التطبيق على إذن تحديد الموقع الجغرافي أو لم يتم تفعيل إعدادات الموقع الجغرافي للجهاز. "<a href="https://support.google.com/android/answer/9319337">"مزيد من المعلومات"</a></string>
</resources>
diff --git a/PermissionController/res/values-as-v34/strings.xml b/PermissionController/res/values-as-v34/strings.xml
index 219f79370..e0acecb4f 100644
--- a/PermissionController/res/values-as-v34/strings.xml
+++ b/PermissionController/res/values-as-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-as-watch/strings.xml b/PermissionController/res/values-as-watch/strings.xml
index 314a08e71..ee41e21d6 100644
--- a/PermissionController/res/values-as-watch/strings.xml
+++ b/PermissionController/res/values-as-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"সলনি কৰিব নোৱাৰি"</string>
<string name="generic_yes" msgid="2489207724988649846">"হয়"</string>
<string name="generic_cancel" msgid="2631708607129269698">"বাতিল কৰক"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"সকলো সময়তে"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"এপ্‌টো ব্যৱহাৰ কৰা সময়ত"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"সকলো সময়তে"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"এপ্‌টো ব্যৱহাৰ কৰা সময়ত"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"সকলো সময়তে"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"এপ্‌টো ব্যৱহাৰ কৰা সময়ত"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"সকলো সময়তে"</string>
</resources>
diff --git a/PermissionController/res/values-as/strings.xml b/PermissionController/res/values-as/strings.xml
index 2656530e4..3c63dd050 100644
--- a/PermissionController/res/values-as/strings.xml
+++ b/PermissionController/res/values-as/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"অনুমতি"</string>
<string name="cancel" msgid="8943320028373963831">"বাতিল কৰক"</string>
<string name="back" msgid="6249950659061523680">"উভতি যাওক"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"বন্ধ কৰক"</string>
<string name="available" msgid="6007778121920339498">"উপলব্ধ"</string>
<string name="blocked" msgid="9195547604866033708">"অৱৰোধ কৰা আছে"</string>
<string name="on" msgid="280241003226755921">"অন আছে"</string>
@@ -34,6 +35,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 +62,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,14 +111,12 @@
<!-- 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>
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"ইনষ্টল/আনইনষ্টল কাৰ্য Wearত কৰিব নোৱাৰি।"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক ক’ত এক্সেছ দিব লাগে বাছনি কৰক"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; আপডে’ট কৰা হৈছে। এই এপক ক’ত এক্সেছ দিব লাগে বাছনি কৰক।"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"বাতিল কৰক"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"সীমিত এক্সেছৰ অনুমতি দিয়ক"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>ত এই এপৰ <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"যদি ব্যৱহাৰ হোৱা নাই এপ্‌টো পৰিচালনা কৰক"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"অনুমতি আঁতৰাওক, অস্থায়ী ফাইল মচক আৰু জাননী বন্ধ কৰক"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"অনুমতি আঁতৰাওক, অস্থায়ী ফাইল মচক, জাননী বন্ধ কৰক আৰু এপ্‌টো আৰ্কাইভ কৰক"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"আপোনাৰ ডেটা সুৰক্ষিত কৰিবলৈ এই এপ্‌টো কেইমাহমান ব্যৱহাৰ নকৰিলে এইটোৰ বাবে থকা অনুমতিসমূহ আঁতৰোৱা হ\'ব।"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"আপোনাৰ ডেটা সুৰক্ষিত কৰিবলৈ এই এপ্‌টো কেইমাহমান ব্যৱহাৰ নকৰিলে তলত উল্লেখ কৰা অনুমতিসমূহ আঁতৰোৱা হ\'ব: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"আপোনাৰ ডেটা সুৰক্ষিত কৰিবলৈ আপুনি কেইমাহমান ব্যৱহাৰ নকৰা এপ্‌সমূহৰ পৰা অনুমতিসমূহ আঁতৰোৱা হ\'ব।"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"আটাইবোৰ ফাইল পৰিচালনা কৰিবলৈ অনুমতি আছে"</string>
<string name="ask_header" msgid="2633816846459944376">"প্ৰতিবাৰতে সোধক"</string>
<string name="denied_header" msgid="903209608358177654">"অনুমতি নাই"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>ত <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"আটাইবোৰ ফাইল এক্সেছ কৰিব পৰা অধিক এপ্‌ চাওক"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{১ দিন}one{# দিন}other{# দিন}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string>
@@ -362,7 +368,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ডিফ’ল্ট Wallet এপ্‌"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet এপ্‌"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Wallet এপে বিভিন্ন ধৰণৰ লেনদেনত সহায় কৰিবলৈ আপোনাৰ ক্ৰেডিট আৰু লয়েল্টী কাৰ্ড, গাড়ীৰ চাবি আৰু আন বস্তু ষ্ট’ৰ কৰিব পাৰে।"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g>ক আপোনাৰ ডিফ’ল্ট Wallet এপ্‌ হিচাপে ছেট কৰিবনে?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"কোনো অনুমতিৰ প্ৰয়োজন নাই"</string>
<string name="request_role_current_default" msgid="738722892438247184">"বৰ্তমানৰ ডিফ’ল্ট"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"পুনৰায় নুসুধিব"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ডিফ’ল্ট ৰূপে ছেট কৰক"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"অধিক ডিফ’ল্ট"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"লিংকসমূহ খুলি থকা হৈছে"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"কৰ্মস্থানৰ বাবে ডিফ’ল্ট"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"প্ৰাইভেট স্পে’চৰ বাবে ডিফ’ল্ট"</string>
<string name="default_app_none" msgid="9084592086808194457">"নাই"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ছিষ্টেম ডিফ\'ল্ট)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"কোনো এপ্‌ নাই"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ত &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ৰ বাবে অৱস্থানৰ এক্সেছ সলনি কৰিবনে?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"এই এপ্‌টোৱে সকলো সময়তে আপোনাৰ অৱস্থান এক্সেছ কৰিবলৈ বিচাৰে, আনকি আপুনি এপ্‌টো ব্যৱহাৰ কৰি নথকা সময়তো। "<annotation id="link">"ছেটিঙত অনুমতি দিয়ক।"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক বিচাৰিবলৈ, সংযোগ কৰিবলৈ আৰু নিকটৱৰ্তী ডিভাইচৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ দিবনে?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ত <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ত &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ৰ বাবে মাইক্ৰ’ফ’নৰ এক্সেছ সলনি কৰিবনে?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ত &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ৰ বাবে কেমেৰাৰ এক্সেছ সলনি কৰিবনে?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ত আপোনাৰ দেহৰ গুৰুত্বপূৰ্ণ অংগসমূহৰ বিষয়ে ছেন্সৰৰ ডেটা এক্সেছ কৰিবলৈ অনুমতি দিবনে?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"এই এপ্‌টোক আপুনি এইটো ব্যৱহাৰ কৰি নথকা সময়কো ধৰি সকলো সময়তে শৰীৰৰ ছেন্সৰৰ ডেটা এক্সেছ কৰিবলৈ দিবলৈ, "<annotation id="link">"ছেটিঙলৈ যাওক।"</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_device_aware_sensors" msgid="5542771499929819675">"এপ্‌টো ব্যৱহাৰ হৈ থকাৰ সময়ত &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ক &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"নাই"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"বিগত\n২৪ ঘণ্টা"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"যোৱা\n৭ দিনত"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> শতাংশ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> Androidৰ দ্বাৰা সুৰক্ষিত। আপোনাৰ ডেটাখিনি এই ডিভাইচটোত প্ৰক্ৰিয়াকৰণ কৰা হয় বাবে স্থিতি দণ্ডত অথবা আপোনাৰ গোপনীয়তাৰ ডেশ্বব’ৰ্ডত এই এপ্‌টোৰ অনুমতিৰ ব্যৱহাৰ দেখুওৱা নহয়।"</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"কেমেৰাৰ এক্সেছ অফ আছে"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"মাইক্ৰ’ফ’নৰ এক্সেছ অফ আছে"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"অৱস্থানৰ এক্সেছ অফ আছে"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ইনফ’টেইনমেণ্ট এপ্‌সমূহৰ বাবে"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"আৱশ্যকীয় এপ্‌সমূহৰ বাবে"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"এই এপ্‌টো আৱশ্যকীয়"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"এই এপ্‌টো আপোনাৰ গাড়ী নিৰ্মাতাৰ বাবে আৱশ্যকীয়"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>ত এক্সেছ কৰিছিল"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"কালি <xliff:g id="TIME_DATE">%1$s</xliff:g>ত এক্সেছ কৰিছিল"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>ত এক্সেছ কৰিছিল"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"আপোনাৰ এবাৰ ব্যৱহাৰযোগ্য পাছৱর্ড হৈছে ১৩২৪৩৫"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"প্ৰতিবন্ধিত ছেটিং"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"আপোনাৰ সুৰক্ষাৰ বাবে, এই ছেটিংটো বৰ্তমান উপলব্ধ নহয়।"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"এপ্‌টোক <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>ৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"এপ্‌টোৱে সংবেদনশীল অনুমতি এক্সেছ কৰিবলৈ অনুৰোধ কৰিছে, যিটোৱে আপোনাৰ ব্যক্তিগত আৰু বিত্তীয় তথ্য বিপদাপন্ন কৰিব পাৰে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এপ্‌টোৱে এই প্ৰতিবন্ধিত অনুমতিটোৰ অবিহনে সঠিককৈ কাম নকৰাটো সম্ভৱ হ’ব পাৰে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কেনেকৈ এক্সেছৰ অনুমতি দিব লাগে জানক&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"এপ্‌টোক ডিফ’ল্ট <xliff:g id="ROLE_NAME">%1$s</xliff:g>ৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"এপ্‌টোৱে সংবেদনশীল অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুৰোধ কৰিছে, যিটোৱে আপোনাৰ ব্যক্তিগত আৰু বিত্তীয় তথ্য বিপদাপন্ন কৰিব পাৰে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এপ্‌টোৱে এই প্ৰতিবন্ধিত অনুমতিসমূহৰ অবিহনে সঠিককৈ কাম নকৰাটো সম্ভৱ হ’ব পাৰে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কেনেকৈ এক্সেছৰ অনুমতি দিব লাগে জানক&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"এপ্‌টোৰ এক্সেছ প্ৰত্যাখ্যান কৰা হৈছে"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"এই অনুমতিটোৰ এক্সেছ প্ৰদান কৰাটোৱে আপোনাৰ ব্যক্তিগত আৰু বিত্তীয় তথ্য বিপদাপন্ন কৰিব পাৰে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এপ্‌টোৱে এই প্ৰতিবন্ধিত অনুমতিটোৰ অবিহনে সঠিককৈ কাম নকৰাটো সম্ভৱ হ’ব পাৰে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কেনেকৈ এক্সেছৰ অনুমতি দিব লাগে জানক&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"অধিক জানক"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ঠিক আছে"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"অনুমতিৰ অনুৰোধ অৱদমন কৰা হৈছে"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"এই এপ্‌টোৱে অতিৰিক্ত অনুমতিৰ বাবে অনুৰোধ কৰিছে, কিন্তু ষ্ট্ৰীমিং ছেশ্বনত অনুমতি দিব নোৱাৰি। আপোনাৰ ফ’নটোত প্ৰথমে অনুমতি দিয়ক।"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"জৰুৰীকালীন কল বা পাঠ বাৰ্তাৰ বাবে"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"অৱস্থান জৰুৰীকালীন সেৱালৈ পঠিওৱা হৈছে"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"এটা জৰুৰীকালীন নম্বৰলৈ কল কৰোঁতে বা বাৰ্তা পঠিয়াওঁতে এই এপ্‌টোৱে আপোনাৰ ডিভাইচৰ অৱস্থান এক্সেছ কৰিছে। এপ্‌টোৰ অৱস্থানৰ অনুমতি নাথাকিলে বা ডিভাইচৰ অৱস্থান অফ হৈ থাকিলেও এনে হ’ব পাৰে। "<a href="https://support.google.com/android/answer/9319337">"অধিক জানক"</a></string>
</resources>
diff --git a/PermissionController/res/values-az-v34/strings.xml b/PermissionController/res/values-az-v34/strings.xml
index 7e403dadc..834a2a002 100644
--- a/PermissionController/res/values-az-v34/strings.xml
+++ b/PermissionController/res/values-az-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Tətbiqin sağlamlıq datasına girişini idarə edin"</string>
<string name="location_settings" msgid="8863940440881290182">"Məkana giriş"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Tətbiq və xidmətlər üçün. Bu ayar deaktivdirsə, təcili nömrəyə zəng etdikdə mikrofon datası yenə də paylaşıla bilər"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Tətbiq və xidmətlər üçün"</string>
</resources>
diff --git a/PermissionController/res/values-az-watch/strings.xml b/PermissionController/res/values-az-watch/strings.xml
index 1b4273903..92d8c0e5f 100644
--- a/PermissionController/res/values-az-watch/strings.xml
+++ b/PermissionController/res/values-az-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Dəyişdirilmədi"</string>
<string name="generic_yes" msgid="2489207724988649846">"Bəli"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Ləğv edin"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Hər zaman"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Tətbiq istifadə edərkən"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Hər zaman"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Tətbiq istifadə edərkən"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Hər zaman"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Tətbiq istifadə edərkən"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Hər zaman"</string>
</resources>
diff --git a/PermissionController/res/values-az/strings.xml b/PermissionController/res/values-az/strings.xml
index a517d458f..d994d4c95 100644
--- a/PermissionController/res/values-az/strings.xml
+++ b/PermissionController/res/values-az/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"icazələr"</string>
<string name="cancel" msgid="8943320028373963831">"Ləğv edin"</string>
<string name="back" msgid="6249950659061523680">"Geri"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Əlçatan"</string>
<string name="blocked" msgid="9195547604866033708">"Bloklanıb"</string>
<string name="on" msgid="280241003226755921">"Aktiv"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Bütün icazələr"</string>
<string name="other_permissions" msgid="2901186127193849594">"Digər tətbiq imkanları"</string>
<string name="permission_request_title" msgid="8790310151025020126">"İcazə sorğusu"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Yükləmə/Sistemdən silmə fəaliyyətləri Wear\'də dəstəklənmir."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin daxil olacağı elementləri seçin"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; güncəlləndi. Bu tətbiqin daxil olacağı elementləri seçin."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Ləğv edin"</string>
@@ -191,12 +193,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Həmişə hamısına icazə verin"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Həmişə soruşulsun"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"İcazə verməyin"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Məhdud girişə icazə verin"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Dəqiq məkan"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Təxmini məkan"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Məkan dəqiq aşkarlansın"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Məkan dəqiq aşkarlanmadıqda tətbiqlər məkanı təxmini müəyyən edir"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> üçün icazə"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Tətbiqə <xliff:g id="PERM">%1$s</xliff:g> icazəsi"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazında bu tətbiq üçün <xliff:g id="PERM">%1$s</xliff:g> girişi"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Bütün <xliff:g id="APP">%1$s</xliff:g> icazələrinə baxın"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Bu icazəyə sahib olan bütün tətbiqlərə baxın"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Assistent üçün mikrofon istifadəsini göstərin"</string>
@@ -204,7 +208,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Tətbiq işlənməyəndə icazə ləğv edilsin"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"İcazələri silin və yer boşaldın"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"İstifadə edilmədikdə tətbiq fəaliyyətini durdurun"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"İstifadə edilmədikdə tətbiqi idarə edin"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"İcazələri silin, müvəqqəti faylları silin və bildirişləri dayandırın"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"İcazələri, müvəqqəti faylları silin, bildirişləri dayandırın və tətbiqi arxivə atın"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Bir neçə ay istifadə etmədiyiniz tətbiqlərdən icazələr datanızın qorunması məqsədilə silinib."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Bir neçə ay istifadə etmədiyiniz tətbiqlərdən icazələr datanızın qorunması məqsədilə silinib: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Bir neçə ay istifadə etmədiyiniz tətbiqlərdən icazələr datanızın qorunması məqsədilə silinib."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Bütün fayllara girişi olanlar"</string>
<string name="ask_header" msgid="2633816846459944376">"Həmişə soruşulsun"</string>
<string name="denied_header" msgid="903209608358177654">"İcazə verilməyib"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazında <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Bütün fayllara giriş edə bilən digər tətbiqlərə baxın"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 gün}other{# gün}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# saat}other{# saat}}"</string>
@@ -401,6 +408,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Qeyd tətbiqi"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Cihazınızda qeydlər aparmağa imkan verən tətbiqlər"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"qeydlər"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Defolt pulqabı tətbiqi"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Pulqabı tətbiqi"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Pulqabı tətbiqləri müxtəlif əməliyyat formaları ilə bağlı kömək etmək üçün kredit və sadiqlik kartlarını, avtomobil açarlarını və digər əşyaları saxlaya bilər."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> defolt pulqabı tətbiqi kimi ayarlansın?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"İcazəyə ehtiyac yoxdur"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Cari defolt"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Bir daha soruşmayın"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Defolt olaraq seçin"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Daha çox defolt"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Linklərin açılması"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"İş üçün defolt"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Şəxsi sahə üçün defolt"</string>
<string name="default_app_none" msgid="9084592086808194457">"Yoxdur"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Sistem defoltu)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Tətbiq yoxdur"</string>
@@ -455,48 +468,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Assistent aktivasiya aşkarlanmasını göstərin"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mikrofonun istifadəsi zamanı səsli yardımı aktiv etmək üçün status panelində ikonanı göstərin"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin cihazdakı foto və mediaya daxil olmasına icazə verilsin?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında foto və mediaya giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə kontaktlara daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında kontaktlara giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazının məkanına giriş icazəsi verilsin?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Tətbiq yalnız ondan istifadə etiyiniz zaman məkanı əldə edə bilər"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazın məkanına daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazının məkanına giriş icazəsi verilsin?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Bu tətbiq hətta ondan istifadə etmədiyiniz zaman belə məkanınıza daxil olmaq istəyə bilər. "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; üçün məkana giriş dəyişdirilsin?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında məkana girişi dəyişdirilsin?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Bu tətbiq hətta ondan istifadə etmədiyiniz zaman belə məkanınıza daxil olmaq istəyir. "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə yaxınlıqdakı cihazları tapmaq, qoşulmaq və nisbi mövqeyini təyin etmək icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında yaxınlıqdakı cihazların nisbi yerini tapmasına, qoşulmasına və müəyyən etməsinə icazə verilsin?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə yaxınlıqdakı cihazları tapmaq, qoşulmaq və nisbi mövqeyini təyin etmək icazəsi verilsin? "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> tətbiqinin məkan girişi təxminidən dəqiqə dəyişdirilsin?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> tətbiqinin &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında məkana girişi təxmini seçimindən dəqiq seçiminə dəyişdirilsin?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazın təxmini məkanına daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazının təxmini məkanına giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Dəqiq"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Təxmini"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə təqvimə daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında təqvimə giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə SMS mesajları göndərmək və onlara baxmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında SMS mesajları göndərib baxmağa icazə verilsin?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə cihazdakı foto, media və fayllara daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında foto, media və fayllara giriş icazəsi verilsin?"</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; tətbiqinin bu cihazdakı &lt;b&gt;foto, video, musiqi və audiolara&lt;/b&gt; girişinə icazə verilsin?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin bu cihazdakı &lt;b&gt;foto, video, musiqi, audio və digər fayllara&lt;/b&gt; girişinə icazə verilsin?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazdakı musiqi və audioya girişinə icazə verilsin?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında musiqi və audioya giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin cihazdakı foto və videolara girişinə icazə verilsin?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında foto və videolara giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə bu cihazda digər foto və videolara daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında daha çox foto və videoya giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə səs yazmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında audio qeydə almağa icazə verilsin?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Tətbiq yalnız ondan istifadə etiyiniz zaman audio yaza biləcək"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə audio yazmaq icazəsi verilsin?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında audio qeydə almağa icazə verilsin?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Bu tətbiq hətta ondan istifadə etmədiyiniz zaman belə audio yazmaq istəyə bilər. "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; üçün mikrofona giriş dəyişdirilsin?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında mikrofona girişi dəyişdirilsin?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Bu tətbiq hətta ondan istifadə etmədiyiniz zaman belə audio yazmaq istəyir. "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin fiziki fəaliyyətinizə daxil olmasına icazə verilsin?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında fiziki fəaliyyətə giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə şəkil və video çəkmək icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında şəkil çəkməyə və video qeydə almağa icazə verilsin?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Tətbiq yalnız ondan istifadə etiyiniz zaman şəkil çəkə və video yaza biləcək"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə şəkil çəkmək və video yazmaq icazəsi verilsin?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında şəkil çəkməyə və video qeydə almağa icazə verilsin?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Bu tətbiq hətta ondan istifadə etmədiyiniz zaman belə şəkil çəkmək və video yazmaq istəyə bilər. "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; üçün kameraya giriş dəyişdirilsin?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında kameraya girişi dəyişdirilsin?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Bu tətbiq hətta ondan istifadə etmədiyiniz zaman belə şəkil çəkmək və video yazmaq istəyir. "<annotation id="link">"Ayarlarda icazə verin."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə telefonun zəng qeydlərinə daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında telefon zəngi qeydlərinə giriş icazəsi verilsin?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə telefon zəngləri etmək və onları idarə etmək icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında telefon zəngi etmək və onu idarə etməyə icazə verilsin?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə həyati əlamətlər haqqında sensor dataya daxil olmaq icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında həyati göstəricilər üzrə sensor datasına giriş icazəsi verilsin?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Bu tətbiq hər zaman, hətta ondan istifadə etmədiyiniz zaman belə sağlamlıq göstəriciləriniz haqqında sensor datasına giriş etmək istəyir. Bu dəyişikliyi etmək üçün "<annotation id="link">"ayarlara keçin"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə sağlamlıq göstəriciləriniz haqqında sensor datasına giriş icazəsi verilsin?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında həyati göstəricilər üzrə sensor datasına giriş icazəsi verilsin?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Tətbiqdən istifadə etmədiyiniz zaman belə, bu tətbiqin bədən sensoru datasına hər zaman giriş etməsinə icazə vermək üçün "<annotation id="link">"ayarlara keçin."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinin istifadə zamanı bədən sensoru datasına giriş etməsinə icazə verilməyə davam edilsin?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Tətbiqdən istifadə edərkən &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında bədən sensoru datasına girişə icazə verilsin?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə sizə bildiriş göndərmək icazəsi verilsin?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tətbiqinə &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında sizə bildiriş göndərməyə icazə verilsin?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"İdarə edilən icazələr"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinin məkan icazəsi var"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Təşkilat <xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinə məkan icazəsi verir"</string>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Heç biri"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Ötən\n24 saat"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Son\n7 gün"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> faiz"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android tərəfindən qorunur. Məlumatlarınız bu cihazda işləndiyinə görə bu tətbiqin icazə istifadəsi status panelində və ya məxfilik panelinizdə göstərilmir."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android tərəfindən qorunur. Məlumatlarınız bu cihazda işləndiyinə görə bu tətbiqin icazə istifadəsi məxfilik panelində göstərilmir."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Cihazın kamerası bloklanıb"</string>
@@ -520,6 +561,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Tətbiqlər və xidmətlər üçün"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Fövqəladə hallar nömrəsinə zəng etdiyiniz zaman mikrofon datası yenə də paylaşıla bilər."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Dəyişin"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kamera girişi deaktivdir"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofona giriş sönülüdür"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Məkana giriş sönülüdür"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Məlumat-əyləncə tətbiqləri üçün"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Lazımi tətbiqlər üçün"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Bu tətbiq tələb edilir"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Avtomobil istehsalçısı bu tətbiqi tələb edir"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Güvənlik və məxfilik"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Cihazı skanlayın"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Qapadın"</string>
@@ -619,4 +667,26 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Data paylaşma yenilikləri"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Bəzi tətbiqlər məkan datasını paylaşma üsulunu dəyişib"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Ayarlar"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Daxil olunub: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Daxil olunub: dünən <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Daxil olunub: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Birdəfəlik parol: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Məhdudlaşdırılmış ayar"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Güvənlik üçün bu ayar əlçatan deyil."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Tətbiqə <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> icazəsinə giriş verilmədi"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Tətbiq şəxsi və maliyyə məlumatlarınızı riskə ata biləcək həssas məlumat icazəsinə giriş istədi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu məhdud icazə olmadan tətbiq düzgün işləməyə bilər. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Girişə icazə vermək haqqında ətraflı&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Tətbiqə defolt <xliff:g id="ROLE_NAME">%1$s</xliff:g> roluna giriş verilmədi"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Tətbiq şəxsi və maliyyə məlumatlarınızı riskə ata biləcək həssas məlumat icazələrinə giriş istədi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu məhdud icazələr olmadan tətbiq düzgün işləməyə bilər. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Girişə icazə vermək haqqında ətraflı&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Tətbiqə giriş verilmədi"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Bu icazəyə giriş şəxsi və maliyyə məlumatlarını riskə ata bilər.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu məhdud icazə olmadan tətbiq düzgün işləməyə bilər. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Girişə icazə vermək haqqında ətraflı&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Ətraflı məlumat"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"İcazə sorğusu dayandırıldı"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Bu tətbiqə əlavə icazələr lazımdır, lakin yayım sessiyasında icazə vermək olmur. Əvvəlcə telefonda icazə verin."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-b+sr+Latn-v34/strings.xml b/PermissionController/res/values-b+sr+Latn-v34/strings.xml
index d5bf45128..e4af6c3e6 100644
--- a/PermissionController/res/values-b+sr+Latn-v34/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Upravljajte pristupom aplikacija podacima o zdravlju"</string>
<string name="location_settings" msgid="8863940440881290182">"Pristup lokaciji"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Za aplikacije i usluge. Ako je ovo podešavanje isključeno, podaci mikrofona mogu i dalje da se dele kada pozovete broj za hitne slučajeve"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Za aplikacije i usluge"</string>
</resources>
diff --git a/PermissionController/res/values-b+sr+Latn-watch/strings.xml b/PermissionController/res/values-b+sr+Latn-watch/strings.xml
index 673a26286..f4b6e53bb 100644
--- a/PermissionController/res/values-b+sr+Latn-watch/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ne može da se promeni"</string>
<string name="generic_yes" msgid="2489207724988649846">"Da"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Otkaži"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Sve vreme"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Pri korišćenju aplikacije"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Sve vreme"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Pri korišćenju aplikacije"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Sve vreme"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Pri korišćenju aplikacije"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Sve vreme"</string>
</resources>
diff --git a/PermissionController/res/values-b+sr+Latn/strings.xml b/PermissionController/res/values-b+sr+Latn/strings.xml
index 48bc61dc5..6f53f5e77 100644
--- a/PermissionController/res/values-b+sr+Latn/strings.xml
+++ b/PermissionController/res/values-b+sr+Latn/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"dozvole"</string>
<string name="cancel" msgid="8943320028373963831">"Otkaži"</string>
<string name="back" msgid="6249950659061523680">"Nazad"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Zatvori"</string>
<string name="available" msgid="6007778121920339498">"Dostupno"</string>
<string name="blocked" msgid="9195547604866033708">"Blokirano"</string>
<string name="on" msgid="280241003226755921">"Uključeno"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Sve dozvole"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ostale mogućnosti aplikacije"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Zahtev za dozvolu"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Radnje Instaliraj/Deinstaliraj nisu podržane u Wear-u."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Izaberite čemu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; može da pristupa"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; je ažurirana. Izaberite čemu ova aplikacija može da pristupa."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Otkaži"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Uvek dozvoli sve"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ne dozvoli"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Dozvoli ograničen pristup"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Precizna lokacija"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Približna lokacija"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Koristi preciznu lokaciju"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Aplikacije mogu da pristupaju vašoj približnoj lokaciji kada je precizna isključena"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> - dozvola"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Pristup ove aplikacije funkciji „<xliff:g id="PERM">%1$s</xliff:g>“"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Ova aplikacija ima pristup za: <xliff:g id="PERM">%1$s</xliff:g> na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Prikaži sve dozvole za: <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Prikaži sve aplikacije sa ovom dozvolom"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Prikaži kako Pomoćnik koristi mikrofon"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Ukloni dozvole ako se aplikacija ne koristi"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Ukloni dozvole i oslobodi prostor"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pauziraj aktivnosti ako se ne koristi"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Upravljajte aplikacijom ako se ne koristi"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Uklonite dozvole, izbrišite privremene fajlove i zaustavite obaveštenja"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Uklonite dozvole, izbrišite privremene fajlove, zaustavite obaveštenja i arhivirajte aplikaciju"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Radi zaštite podataka, dozvole za ovu aplikaciju se uklanjaju ako se aplikacija ne koristi par meseci."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Radi zaštite podataka, sledeće dozvole se uklanjaju ako se aplikacija ne koristi par meseci: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Radi zaštite podataka, dozvole su uklonjene iz aplikacija koje niste koristili par meseci."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Imaju dozvolu za upravljanje svim datotekama"</string>
<string name="ask_header" msgid="2633816846459944376">"Pitaj svaki put"</string>
<string name="denied_header" msgid="903209608358177654">"Nije dozvoljeno"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Prikaži još aplikacija sa pristupom svim fajlovima"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string>
@@ -373,7 +379,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 +391,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikacija za beleške"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikacije koje vam omogućavaju da pravite beleške na uređaju"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"beleške"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Podrazumevana aplikacija novčanika"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikacija Novčanik"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikacije novčanika mogu da čuvaju vaše kreditne kartice i kartice lojalnosti, ključeve od automobila i druge stvari kako bi vam pomogli pri različitim transakcijama."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Želite da podesite <xliff:g id="APP_NAME">%1$s</xliff:g> kao podrazumevanu aplikaciju novčanika?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nije potrebna nijedna dozvola"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Trenutno podrazumevana"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ne pitaj ponovo"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Podesi kao podrazum."</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Još podrazumevanih aplikacija"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Otvaranje linkova"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Podrazumevana za posao"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Podrazumevano za privatan prostor"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ništa"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Podrazumevana sistemska)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nema aplikacija"</string>
@@ -436,7 +448,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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Prikazuj otkrivanje aktiviranja pomoćnika"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Prikazuje ikonu na statusnoj traci kada se mikrofon koristi za aktiviranje glasovnog pomoćnika"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Želite li da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa slikama i medijima na uređaju?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa slikama i medijima na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa kontaktima?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa kontaktima na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacija će imati pristup lokaciji samo dok koristite aplikaciju"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ova aplikacija možda želi da pristupa lokaciji sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Želite li da promenite pristup lokaciji za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Menjate pristup lokaciji za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ova aplikacija želi da pristupa lokaciji sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Dozvoljavate da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pronalazi uređaje u blizini, povezuje se s njima i određuje im relativan položaj?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pronalazi uređaje u blizini, povezuje se sa njima i utvrđuje njihovu relativnu poziciju na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Dozvoljavate da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pronalazi uređaje u blizini, povezuje se s njima i određuje im relativan položaj? "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Želite li da promenite pristup aplikacije <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> iz približne lokacije na preciznu?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Menjate pristup aplikacije <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; iz približne u preciznu lokaciju?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Želite li da omogućite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa približnoj lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa približnoj lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precizna"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Približna"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Želite da dozvolite da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa kalendaru?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa kalendaru na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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_device_aware_sms" msgid="6639977653040502291">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje i pregleda SMS poruke na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_storage" msgid="6933251810928606636">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa slikama, medijima i fajlovima na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_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_device_aware_read_media_aural" msgid="7927884506238101064">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa muzici i zvuku na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Dozvoljavate da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa slikama i videima na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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 videima na ovom uređaju?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa i drugim slikama i videima na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima zvuk na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ova aplikacija možda želi da snima zvuk sve vreme, čak i kada ne koristite aplikaciju. "<annotation id="link">"Dozvolite u podešavanjima."</annotation></string>
<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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Menjate pristup mikrofonu za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa podacima o fizičkim aktivnostima na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snima slike i video na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Menjate pristup kameri za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_calllog" msgid="8220927190376843309">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa evidenciji telefonskih poziva na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; upućuje telefonske pozive na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa podacima senzora o vitalnim funkcijama na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa podacima senzora o vitalnim funkcijama na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Dozvoljavate da aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tokom korišćenja i dalje pristupa podacima senzora za telo na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Želite da dozvolite da vam &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje obaveštenja?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Dozvoljavate da vam aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje obaveštenja na: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrolisane dozvole"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> ima pristup lokaciji"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organizacija dozvoljava da <xliff:g id="APP_NAME">%1$s</xliff:g> pristupa lokaciji"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ništa"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Prethodna\n24 sata"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"U prethodnih\n7 dana"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> posto"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Android štiti <xliff:g id="APP_NAME">%1$s</xliff:g>. Pošto se podaci obrađuju na ovom uređaju, korišćenje dozvola za ovu aplikaciju se ne prikazuje na statusnoj traci ili kontrolnoj tabli za privatnost."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Android štiti <xliff:g id="APP_NAME">%1$s</xliff:g>. Pošto se podaci obrađuju na ovom uređaju, korišćenje dozvola za ovu aplikaciju se ne prikazuje na kontrolnoj tabli za privatnost."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera uređaja je blokirana"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Za aplikacije i usluge"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Podaci mikrofona mogu i dalje da se dele kada pozovete broj za hitne slučajeve."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Promeni"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Pristup kameri je isključen"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Pristup mikrofonu je isključen"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Pristup lokaciji je isključen"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Za aplikacije za informacije i zabavu"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Za obavezne aplikacije"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ova aplikacija je obavezna"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ovu aplikaciju zahteva proizvođač automobila"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Bezbednost i privatnost"</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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Ažuriranja za deljenje podataka"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Neke aplikacije su promenile način na koji mogu da dele podatke o lokaciji"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Podešavanja"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Pristupano: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pristupano juče: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pristupano: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Jednokratna lozinka je 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničeno podešavanje"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ovo podešavanje je trenutno nedostupno radi vaše bezbednosti."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji nije dozvoljen pristup dozvoli: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup osetljivoj dozvoli, što može da ugrozi bezbednost ličnih i finansijskih podataka.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija možda neće raditi ispravno bez ove ograničene dozvole. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako da dozvolite pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji nije dozvoljen pristup da postane podrazumevana: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup osetljivim dozvolama, što može da ugrozi bezbednost ličnih i finansijskih podataka.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija možda neće raditi ispravno bez ovih ograničenih dozvola. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako da dozvolite pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaciji nije dozvoljen pristup"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup ovoj dozvoli može da ugrozi bezbednost ličnih i finansijskih podataka.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija možda neće raditi ispravno bez ove ograničene dozvole. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako da dozvolite pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saznajte više"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Važi"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Zahtev za dozvolu je blokiran"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ova aplikacija zahteva dodatne dozvole, ali dozvole ne mogu da se daju u sesiji strimovanja. Prvo dajte dozvolu na telefonu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za hitni poziv ili poruku"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija je poslata hitnim službama"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ova aplikacija je pristupila lokaciji uređaja tokom pozivanja broja za hitne slučajeve ili slanja poruke tom broju. To može da se dogodi čak i kada aplikacija nema dozvolu za lokaciju ili je lokacija uređaja isključena. "<a href="https://support.google.com/android/answer/9319337">"Saznajte više"</a></string>
</resources>
diff --git a/PermissionController/res/values-be-v34/strings.xml b/PermissionController/res/values-be-v34/strings.xml
index 330681ded..9d46314e6 100644
--- a/PermissionController/res/values-be-v34/strings.xml
+++ b/PermissionController/res/values-be-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-be-watch/strings.xml b/PermissionController/res/values-be-watch/strings.xml
index 242f6306d..c237932f5 100644
--- a/PermissionController/res/values-be-watch/strings.xml
+++ b/PermissionController/res/values-be-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Не ўдалося змяніць"</string>
<string name="generic_yes" msgid="2489207724988649846">"Так"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Скасаваць"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Заўсёды"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Пры выкарыстанні праграмы"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Заўсёды"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Пры выкарыстанні праграмы"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Заўсёды"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Пры выкарыстанні праграмы"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Заўсёды"</string>
</resources>
diff --git a/PermissionController/res/values-be/strings.xml b/PermissionController/res/values-be/strings.xml
index c6e081982..7135b8ac9 100644
--- a/PermissionController/res/values-be/strings.xml
+++ b/PermissionController/res/values-be/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"дазволы"</string>
<string name="cancel" msgid="8943320028373963831">"Скасаваць"</string>
<string name="back" msgid="6249950659061523680">"Назад"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Закрыць"</string>
<string name="available" msgid="6007778121920339498">"Даступна"</string>
<string name="blocked" msgid="9195547604866033708">"Заблакіравана"</string>
<string name="on" msgid="280241003226755921">"Уключана"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Дзеянні па ўсталяванні або выдаленні не падтрымліваюцца на Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Выберыце, да чаго дазволіць доступ праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Праграма &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; была абноўлена. Выберыце, да чаго ёй дазволіць доступ."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Скасаваць"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"Дазволіць абмежаваны доступ"</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_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_with_device_name" msgid="7193042925656173271">"Дазвол \"<xliff:g id="PERM">%1$s</xliff:g>\" для гэтай праграмы на прыладзе \"<xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Кіраваць праграмай, якой не карыстаюцца"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Выдаліць дазволы, часовыя файлы і спыніць апавяшчэнні"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Выдаліць дазволы і часовыя файлы, спыніць адпраўку апавяшчэнняў і архіваваць праграму"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Калі праграма не выкарыстоўваецца на працягу некалькіх месяцаў, то ў мэтах абароны вашых даных з яе будуць выдалены дазволы."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Калі праграма не выкарыстоўваецца на працягу некалькіх месяцаў, у мэтах абароны вашых даных будуць выдалены наступныя дазволы: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"У мэтах абароны вашых даных выдалены дазволы для праграм, якія не выкарыстоўваліся некалькі месяцаў."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Дазволена кіраванне ўсімі файламі"</string>
<string name="ask_header" msgid="2633816846459944376">"Заўсёды пытацца"</string>
<string name="denied_header" msgid="903209608358177654">"Забароненыя"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"Група дазволаў \"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>\" на прыладзе \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Праглядзець іншыя праграмы, якія маюць доступ да ўсіх файлаў"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 дзень}one{# дзень}few{# дні}many{# дзён}other{# дня}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# гадзіна}one{# гадзіна}few{# гадзіны}many{# гадзін}other{# гадзіны}}"</string>
@@ -362,7 +368,7 @@
<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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Стандартная праграма-кашалёк"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Праграма-кашалёк"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"У праграмах-кашальках можна захоўваць крэдытныя карткі, карткі лаяльнасці, ключы ад аўтамабіля і іншыя лічбавыя аб\'екты, што дае магчымасць ажыццяўляць розныя формы трансакцый."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Задаць у якасці стандартнай праграмы-кашалька праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Дазволы не патрэбныя"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Цяперашняя стандартная"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Больш не пытацца"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Зрабіць стандартнай"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Дадатковыя стандартныя налады"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Адкрыццё спасылак"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Стандартныя для працы"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Стандартныя праграмы для прыватнай прасторы"</string>
<string name="default_app_none" msgid="9084592086808194457">"Няма"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Стандартная сістэмная)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Няма праграм"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да фота і мультымедыя на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да кантактаў на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да геаданых на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да геаданых на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Змяніць для праграмы &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; налады доступу да даных пра месцазнаходжанне на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; знаходзіць прылады паблізу, падключацца да іх і вызначаць іх адноснае месцазнаходжанне на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"Дазволіць праграме \"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\" доступ да даных пра дакладнае месцазнаходжанне прылады &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да прыблізнага месцазнаходжання на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да календара на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; адпраўляць і праглядаць SMS-паведамленні на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да фота, мультымедыя і файлаў на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да музыкі і аўдыя на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да фота і відэа на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да большай колькасці фота і відэа на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; запісваць аўдыя на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; запісваць аўдыя на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Змяніць для праграмы &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; налады доступу да мікрафона на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да даных аб фізічнай актыўнасці на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; знімаць фота і запісваць відэа на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; знімаць фота і запісваць відэа на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Змяніць для праграмы &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; налады доступу да камеры на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да журналаў выклікаў на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; рабіць тэлефонныя выклікі і кіраваць імі на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да даных датчыкаў з паказчыкамі арганізма на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ да даных датчыкаў з паказчыкамі арганізма на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Каб дазволіць гэтай праграме мець пастаянны доступ да даных датчыкаў цела, нават калі яна не выкарыстоўваецца, "<annotation id="link">"перайдзіце ў налады."</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_device_aware_sensors" msgid="5542771499929819675">"Пакінуць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; дазвол на доступ да даных датчыкаў цела на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Дазволіць праграме &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; адпраўляць вам апавяшчэнні на прыладзе &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Няма"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"За апошнія\n24 гадзіны"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"За апошнія\n7 сутак"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>, працэнтаў: <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" абаронена Android. З прычыны таго, што вашы даныя апрацоўваюцца на гэтай прыладзе, на панэлі стану і панэлі кіравання доступам не паказваецца інфармацыя пра выкарыстанне дазволаў гэтай праграмай."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Доступ да камеры забаронены"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Доступ да мікрафона выключаны"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Доступ да месцазнаходжання выключаны"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Для праграм інфармацыйна-забаўляльнай сістэмы"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Для абавязковых праграм"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Гэта праграма абавязковая"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Выкарыстання гэтай праграмы патрабуе вытворца аўтамабіля"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Апошні раз доступ быў атрыманы ў <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Апошні раз доступ быў атрыманы ўчора ў <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Апошні раз доступ быў атрыманы ў <xliff:g id="TIME_DATE_1">%2$s</xliff:g> <xliff:g id="TIME_DATE_0">%1$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Ваш аднаразовы пароль: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Налада з абмежаваным доступам"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"У мэтах бяспекі гэта налада цяпер недаступная."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Праграме адмоўлена ў дазволе \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Праграма запытала дазвол на доступ да канфідэнцыяльнай інфармацыі. Калі вы яго дасце, ваша асабістая і фінансавая інфармацыя можа аказацца ў небяспецы.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Магчыма, без гэтага абмежаванага дазволу праграма не будзе працаваць належным чынам. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Даведацца, як дазволіць доступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Праграме адмоўлена ў дазволе стандартна выконваць наступную ролю: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Праграма запытала дазволы на доступ да канфідэнцыяльнай інфармацыі. Калі вы іх дасце, ваша асабістая і фінансавая інфармацыя можа аказацца ў небяспецы.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Магчыма, без гэтых абмежаваных дазволаў праграма не будзе працаваць належным чынам. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Даведацца, як дазволіць доступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Праграме адмоўлена ў дазволе"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Калі вы дасце гэты дазвол, ваша асабістая і фінансавая інфармацыя можа аказацца ў небяспецы.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Магчыма, без гэтага абмежаванага дазволу праграма не будзе працаваць належным чынам. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Даведацца, як дазволіць доступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Даведацца больш"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Запыт дазволаў заблакіраваны"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Гэта праграма запытвае дадатковыя дазволы, якія немагчыма даць падчас трансляцыі. Спачатку дайце дазвол на тэлефоне."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Для выкліку або SMS экстранным службам"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Геаданыя адпраўляюцца экстранным службам"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Калі вы тэлефанавалі або адпраўлялі SMS на нумар экстраннай службы, гэта праграма атрымлівала доступ да геаданых прылады. Гэта магчыма, нават калі ў праграмы няма дазволу на доступ да геаданых і калі вызначэнне геаданых прылады выключана. "<a href="https://support.google.com/android/answer/9319337">"Даведацца больш"</a></string>
</resources>
diff --git a/PermissionController/res/values-bg-v34/strings.xml b/PermissionController/res/values-bg-v34/strings.xml
index 33b3a52f0..6b7d5fea9 100644
--- a/PermissionController/res/values-bg-v34/strings.xml
+++ b/PermissionController/res/values-bg-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-bg-watch/strings.xml b/PermissionController/res/values-bg-watch/strings.xml
index d09f250c4..48692ee7b 100644
--- a/PermissionController/res/values-bg-watch/strings.xml
+++ b/PermissionController/res/values-bg-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Без промяна"</string>
<string name="generic_yes" msgid="2489207724988649846">"Да"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Отказ"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"През цялото време"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"При използване на прилож."</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"През цялото време"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"При използване на прилож."</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"През цялото време"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"При използване на прилож."</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"През цялото време"</string>
</resources>
diff --git a/PermissionController/res/values-bg/strings.xml b/PermissionController/res/values-bg/strings.xml
index a8c0e6315..e32cd1ea4 100644
--- a/PermissionController/res/values-bg/strings.xml
+++ b/PermissionController/res/values-bg/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"разрешения"</string>
<string name="cancel" msgid="8943320028373963831">"Отказ"</string>
<string name="back" msgid="6249950659061523680">"Назад"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Затваряне"</string>
<string name="available" msgid="6007778121920339498">"Налице"</string>
<string name="blocked" msgid="9195547604866033708">"Блокирано"</string>
<string name="on" msgid="280241003226755921">"Вкл."</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Действията инсталиране и деинсталиране не се поддържат на устройства с Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Изберете до какво да има достъп &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Приложението &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; е актуализирано. Изберете до какво да има достъп."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Отказ"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"Разрешаване на ограничения достъп"</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_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_with_device_name" msgid="7193042925656173271">"Достъп до <xliff:g id="PERM">%1$s</xliff:g> на <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Управление на прилож., ако не се ползва"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Премахване на разрешенията, изтриване на временните файлове и спиране на известията"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Премахване на разрешенията, изтриване на временните файлове, спиране на известията и архивиране на приложението"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"За да защитим данните ви, разрешенията за това приложение ще бъдат премахнати, ако не го използвате няколко месеца."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"За да защитим данните ви, следните разрешения ще бъдат премахнати, ако не използвате приложението няколко месеца: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"За да защитим данните ви, премахнахме разрешенията за приложенията, които не сте използвали от няколко месеца."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"С разрешение за управление на всички файлове"</string>
<string name="ask_header" msgid="2633816846459944376">"Запитване всеки път"</string>
<string name="denied_header" msgid="903209608358177654">"Не е разрешено"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> на <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Вижте още приложения, които имат достъп до всички файлове"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ден}other{# дни}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# час}other{# часа}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Основно приложение за портфейл"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Приложение за портфейл"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Приложенията за портфейли могат да съхраняват кредитни карти, карти за лоялност, ключове за автомобил и други неща, за да ви помагат при различни видове транзакции."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Да се зададе ли <xliff:g id="APP_NAME">%1$s</xliff:g> като основно приложение за портфейл?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Не са необходими разрешения"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Текущо стандартно приложение"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Без повторно питане"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Задаване"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Още стандартни приложения"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Отваряне на връзки"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"По подразбиране за работа"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Стандартни за частното пространство"</string>
<string name="default_app_none" msgid="9084592086808194457">"Няма"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Стандартно за системата)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Няма приложения"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до снимките и мултимедията на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до контактите ви на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до местоположението на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до местоположението на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Да се промени ли достъпът на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до местоположението на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да намира, да се свързва със и да определя относителната позиция на устройствата в близост до &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"Да се промени ли достъпът на <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> до местоположението на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до приблизителното местоположение на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до календара ви на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да изпраща и преглежда SMS съобщения на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до снимките, мултимедията и файловете на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до музиката и аудиото на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до снимките и видеоклиповете на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до повече снимки и видеоклипове на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да записва аудио на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да записва аудио на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Да се промени ли достъпът на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до микрофона на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до физическата ви активност на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да прави снимки и да записва видеоклипове на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да прави снимки и да записва видеоклипове на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Да се промени ли достъпът на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до камерата на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до списъците с телефонните ви обаждания на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да извършва и управлява телефонни обаждания на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до данните от сензорите за жизнените ви показатели на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да осъществява достъп до данните от сензорите за жизнените ви показатели на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"За да разрешите на това приложение да осъществява достъп до данните от сензорите за тяло по всяко време – дори когато не го използвате, "<annotation id="link">"отворете настройките"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Да има ли достъп &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до данните от сензорите за тяло на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Да се разреши ли на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да ви изпраща известия на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Без"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Последните\n24 часа"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Последните\n7 дни"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> процента"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Приложението <xliff:g id="APP_NAME">%1$s</xliff:g> е защитено от Android. Тъй като данните ви се обработват на това устройство, използването на разрешенията на приложението не се показва в лентата на състоянието, нито в таблото ви за управление на поверителността."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Достъпът до камерата е изключен"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Достъпът до микрофона е изключен"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Достъпът до местоположението е изключен"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"За приложения за информационно-развлекателната система"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"За изискваните приложения"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Това приложение е необходимо"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Това приложение се изисква от производителя на автомобила ви"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Осъществен достъп: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Осъществен достъп: вчера, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Осъществен достъп: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Еднократната ви парола е 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничена настройка"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"От съображения за сигурност понастоящем тази настройка не е налице."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"На приложението бе отказан достъп до <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Приложението поиска разрешение за достъп до поверителни данни, което може да изложи на риск личната или финансовата ви информация.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Възможно е приложението да не работи правилно без това ограничено разрешение. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Научете как да разрешите достъпа&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"На приложението бе забранено да изпълнява функциите на основно <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложението поиска разрешения за достъп до поверителни данни, които може да изложат на риск личната или финансовата ви информация.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Възможно е приложението да не работи правилно без тези ограничени разрешения. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Научете как да разрешите достъпа&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"На приложението бе отказан достъп"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Достъпът до това разрешение може да изложи на риск личната и финансовата ви информация.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Възможно е приложението да не работи правилно без това ограничено разрешение. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Научете как да разрешите достъпа&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Научете повече"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Заявката за разрешения е блокирана"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Това приложение изисква допълнителни разрешения, но те не могат да бъдат предоставени в сесия за поточно предаване. Първо ги предоставете на телефона си."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"За спешно обаждане или изпращане на съобщение"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Местоположението е изпратено до службите за спешни случаи"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Това приложение осъществи достъп до местоположението на устройството ви по време на обаждане или изпращане на текстово съобщение до номер за спешни случаи. Това може да се случи дори когато приложението няма разрешение за достъп до местоположението или местоположението на устройството е изключено. "<a href="https://support.google.com/android/answer/9319337">"Научете повече"</a></string>
</resources>
diff --git a/PermissionController/res/values-bn-v34/strings.xml b/PermissionController/res/values-bn-v34/strings.xml
index c93980dd1..69ea5b8a9 100644
--- a/PermissionController/res/values-bn-v34/strings.xml
+++ b/PermissionController/res/values-bn-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-bn-watch/strings.xml b/PermissionController/res/values-bn-watch/strings.xml
index f8c2968f8..698bdc32c 100644
--- a/PermissionController/res/values-bn-watch/strings.xml
+++ b/PermissionController/res/values-bn-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"পাল্টানো যাবে না"</string>
<string name="generic_yes" msgid="2489207724988649846">"হ্যাঁ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"বাতিল করুন"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"সব সময়ের জন্য"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"অ্যাপ ব্যবহার করার সময়"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"সব সময়ের জন্য"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"অ্যাপ ব্যবহার করার সময়"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"সব সময়ের জন্য"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"অ্যাপ ব্যবহার করার সময়"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"সব সময়ের জন্য"</string>
</resources>
diff --git a/PermissionController/res/values-bn/strings.xml b/PermissionController/res/values-bn/strings.xml
index a73f1f7ae..b18f03773 100644
--- a/PermissionController/res/values-bn/strings.xml
+++ b/PermissionController/res/values-bn/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"অনুমতি"</string>
<string name="cancel" msgid="8943320028373963831">"বাতিল করুন"</string>
<string name="back" msgid="6249950659061523680">"ফিরুন"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"উপলভ্য"</string>
<string name="blocked" msgid="9195547604866033708">"ব্লক করা হয়েছে"</string>
<string name="on" msgid="280241003226755921">"চালু করা আছে"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear-এ ইনস্টল/আনইনস্টল করা যাবে না।"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপটিকে কিসে কিসে অ্যাক্সেস দেবেন তা বেছে নিন"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; আপডেট করা হয়েছে৷ অ্যাপটিকে কিসে কিসে অ্যাক্সেস দেবেন তা বেছে নিন।"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"বাতিল করুন"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"সীমিত অ্যাক্সেসের অনুমতি দিন"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এ এই অ্যাপের জন্য <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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"ব্যবহার না করা হলে অ্যাপ ম্যানেজ করুন"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"অনুমতি সরান, অস্থায়ী ফাইল মুছুন এবং বিজ্ঞপ্তি বন্ধ করুন"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"অনুমতি সরান, অস্থায়ী ফাইল মুছুন, বিজ্ঞপ্তি বন্ধ করুন এবং অ্যাপ আর্কাইভ করুন"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"আপনার ডেটা সুরক্ষিত রাখতে এই অ্যাপ কয়েক মাস ব্যবহার করা না হলে, এটিকে দেওয়া অনুমতি প্রত্যাহার করে নেওয়া হবে।"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"আপনার ডেটা সুরক্ষিত রাখতে অ্যাপটি কয়েক মাস ব্যবহার করা না হলে, এই অনুমতি প্রত্যাহার করে নেওয়া হবে: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"আপনার ডেটা সুরক্ষিত রাখতে যেসব অ্যাপ গত কয়েক মাস ব্যবহার করা হয়নি সেগুলি থেকে অনুমতি প্রত্যাহার করে নেওয়া হয়েছে।"</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"সমস্ত ফাইল ম্যানেজ করার অনুমতি দেওয়া হয়েছে"</string>
<string name="ask_header" msgid="2633816846459944376">"প্রতিবার জিজ্ঞাসা করে"</string>
<string name="denied_header" msgid="903209608358177654">"অনুমোদিত নয়"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এ <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"সব ফাইল অ্যাক্সেস করতে পারবে এমন আরও অ্যাপ দেখুন"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{১ দিন}one{# দিন}other{# দিন}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ঘণ্টা}one{# ঘণ্টা}other{# ঘণ্টা}}"</string>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ডিফল্ট ওয়ালেট অ্যাপ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"ওয়ালেট অ্যাপ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"বিভিন্ন ধরনের ট্রানজ্যাকশনের ক্ষেত্রে আপনাকে সাহায্য করতে, ওয়ালেট অ্যাপ আপনার ক্রেডিট ও লয়ালটি কার্ড, গাড়ির \'কী\' এবং অন্যান্য জিনিস সেভ করতে পারবে।"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপকে আপনার ডিফল্ট ওয়ালেট অ্যাপ হিসেবে সেট করবেন?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"কোনও অনুমতির প্রয়োজন নেই"</string>
<string name="request_role_current_default" msgid="738722892438247184">"বর্তমান ডিফল্ট"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"আর দেখতে চাই না"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ডিফল্ট হিসেবে রাখুন"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"আরও ডিফল্ট"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"লিঙ্ক খোলা"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"অফিসের জন্য ডিফল্ট"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"প্রাইভেট স্পেসের জন্য ডিফল্ট"</string>
<string name="default_app_none" msgid="9084592086808194457">"কোনওটিই নয়"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(সিস্টেম ডিফল্ট)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"কোনও অ্যাপ নেই"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপের লোকেশন অ্যাক্সেস পরিবর্তন করবেন?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"এই অ্যাপটি সবসময় আপনার লোকেশন অ্যাক্সেস করতে চায়, এমনকি আপনার অ্যাপ চালু না থাকলেও। "<annotation id="link">"সেটিংস বিকল্প থেকে অনুমতি দিন।"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"কাছাকাছি ডিভাইস খুঁজে দেখতে, কানেক্ট করতে এবং সেটির আপেক্ষিক অবস্থান নির্ধারণ করতে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে অনুমতি দেবেন?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে আশেপাশের ডিভাইস খুঁজতে, কানেক্ট করতে এবং সেটি কোথায় রয়েছে তা জানার অনুমতি দেবেন?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"কাছাকাছি ডিভাইস খুঁজে দেখতে, কানেক্ট করতে এবং সেটির আপেক্ষিক অবস্থান নির্ধারণ করতে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে অনুমতি দেবেন? "<annotation id="link">"সেটিংস থেকে অনুমতি দিন।"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>-এ লোকেশন অ্যাক্সেস, আনুমানিক থেকে সুনির্দিষ্ট লোকেশনে পরিবর্তন করতে চান?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপের মাইক্রোফোন অ্যাক্সেস পরিবর্তন করবেন?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপের ক্যামেরা অ্যাক্সেস পরিবর্তন করতে চান?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে আপনার ভাইটাল সাইন সম্পর্কিত সেন্সর ডেটা অ্যাক্সেসের অনুমতি দেবেন?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"অ্যাপটি ব্যবহার না করলেও, সেটিকে সবসময় বডি সেন্সর ডেটাতে অ্যাক্সেস দিতে "<annotation id="link">"সেটিংসে যান"</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_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ডিভাইসে &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপ ব্যবহার করার সময় সেটির বডি সেন্সর ডেটা অ্যাক্সেসের অনুমতি চালু রাখবেন?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-কে বিজ্ঞপ্তি পাঠানোর অনুমতি দেবেন?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; অ্যাপকে &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"কোনওটিই নয়"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"গত\n২৪ ঘণ্টা"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"গত\n৭ দিনে"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> শতাংশ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g>, Android-এর মাধ্যমে সুরক্ষিত। এই ডিভাইসে আপনার ডেটা প্রসেস হওয়ার জন্য, এই অ্যাপের অনুমতি ব্যবহার সংক্রান্ত তথ্য স্ট্যাটাস বার বা প্রাইভেসি ড্যাশবোর্ডে দেখা যাবে না।"</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"ক্যামেরার অ্যাক্সেস বন্ধ করা আছে"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"মাইক্রোফোন অ্যাক্সেস করার সুবিধা বন্ধ আছে"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"লোকেশন অ্যাক্সেস করার সুবিধা বন্ধ আছে"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ইনফোটেনমেন্ট অ্যাপের জন্য"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"প্রয়োজনীয় অ্যাপের জন্য"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"এই অ্যাপটি প্রয়োজন"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"আপনার গাড়ির প্রস্তুতকারকের এই অ্যাপটি প্রয়োজন"</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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>-এ অ্যাক্সেস করা হয়েছে"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"গতকাল <xliff:g id="TIME_DATE">%1$s</xliff:g>-এ অ্যাক্সেস করা হয়েছে"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>-এ অ্যাক্সেস করা হয়েছে"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"আপনার একবার ব্যবহারের পাসওয়ার্ড হল ১২৩৪৩৫"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"বিধিনিষেধযুক্ত সেটিংস"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"আপনার নিরাপত্তার জন্য, এই সেটিং বর্তমানে উপলভ্য নেই।"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"অ্যাপকে <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> অ্যাক্সেস করতে দেওয়া হয়নি"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"অ্যাপটি সংবেদনশীল অনুমতি অ্যাক্সেস করার অনুরোধ জানিয়েছে, যার জন্য আপনার ব্যক্তিগত ও আর্থিক অবস্থা সম্পর্কিত তথ্যের ক্ষেত্রে ঝুঁকির সম্ভাবনা থাকতে পারে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এই সীমাবদ্ধ অনুমতি ছাড়া অ্যাপটি সঠিকভাবে কাজ না করার সম্ভাবনাও আছে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কীভাবে অ্যাক্সেস করার অনুমতি দেবেন সেই সম্পর্কে জানুন&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"অ্যাপকে ডিফল্ট <xliff:g id="ROLE_NAME">%1$s</xliff:g> হিসেবে কাজ করার অ্যাক্সেস দেওয়া হয়নি"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"অ্যাপটি সংবেদনশীল অনুমতি অ্যাক্সেস করার অনুরোধ জানিয়েছে, যার জন্য আপনার ব্যক্তিগত ও আর্থিক অবস্থা সম্পর্কিত তথ্যের ক্ষেত্রে ঝুঁকির সম্ভাবনা থাকতে পারে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এইসব সীমাবদ্ধ অনুমতি ছাড়া অ্যাপটি সঠিকভাবে কাজ না করার সম্ভাবনাও আছে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কীভাবে অ্যাক্সেস করার অনুমতি দেবেন সেই সম্পর্কে জানুন&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"অ্যাপকে অ্যাক্সেস দেওয়া হয়নি"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"এই অনুমতি অ্যাক্সেস করলে আপনার ব্যক্তিগত ও আর্থিক অবস্থা সম্পর্কিত তথ্যের ক্ষেত্রে ঝুঁকি তৈরি হতে পারে।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>এই অনুমতি ছাড়া এই অ্যাপ সঠিকভাবে কাজ না করার সম্ভাবনা থাকতে পারে। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;কীভাবে অ্যাক্সেস করার অনুমতি দেবেন সেই সম্পর্কে জানুন&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"আরও জানুন"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ঠিক আছে"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"অনুমতির অনুরোধ ব্লক করা হয়েছে"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"এই অ্যাপ অতিরিক্ত অনুমতির জন্য অনুরোধ করছে, কিন্তু স্ট্রিমিং সেশন চলাকালীন অনুমতি দেওয়া যাবে না। প্রথমে আপনার ফোনে অনুমতি দিন।"</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-bs-v34/strings.xml b/PermissionController/res/values-bs-v34/strings.xml
index e2b8445df..d3e33f8e9 100644
--- a/PermissionController/res/values-bs-v34/strings.xml
+++ b/PermissionController/res/values-bs-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Upravljajte pristupom aplikacije zdravstvenim podacima"</string>
<string name="location_settings" msgid="8863940440881290182">"Pristup lokaciji"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Za aplikacije i usluge. Ako je ova postavka isključena, podaci mikrofona se i dalje mogu dijeliti kada pozovete broj za hitne slučajeve"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Za aplikacije i usluge"</string>
</resources>
diff --git a/PermissionController/res/values-bs-watch/strings.xml b/PermissionController/res/values-bs-watch/strings.xml
index 1a15ad0b3..68f066963 100644
--- a/PermissionController/res/values-bs-watch/strings.xml
+++ b/PermissionController/res/values-bs-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ne mijenja se"</string>
<string name="generic_yes" msgid="2489207724988649846">"Da"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Otkaži"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Cijelo vrijeme"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Dok se aplikacija koristi"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Cijelo vrijeme"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Dok se aplikacija koristi"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Cijelo vrijeme"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Dok se aplikacija koristi"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Cijelo vrijeme"</string>
</resources>
diff --git a/PermissionController/res/values-bs/strings.xml b/PermissionController/res/values-bs/strings.xml
index 069c6e418..0b9a62365 100644
--- a/PermissionController/res/values-bs/strings.xml
+++ b/PermissionController/res/values-bs/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"odobrenja"</string>
<string name="cancel" msgid="8943320028373963831">"Otkaži"</string>
<string name="back" msgid="6249950659061523680">"Nazad"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Zatvori"</string>
<string name="available" msgid="6007778121920339498">"Dostupno"</string>
<string name="blocked" msgid="9195547604866033708">"Blokirano"</string>
<string name="on" msgid="280241003226755921">"Uključeno"</string>
@@ -29,11 +30,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Sva odobrenja"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ostale mogućnosti aplikacije"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Zahtjev za odobrenje"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Instaliranje/deinstaliranje nije podržano na Wearu."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Izaberite čemu aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;Lt;/b&gt; može pristupiti"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikacija &amp;Lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;Lt;/b&gt; je ažurirana. Izaberite čemu ova aplikacija može pristupiti."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Otkaži"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Uvijek dozvoli sve"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nemoj dozvoliti"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Dozvoli ograničeni pristup"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Tačna lokacija"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Približna lokacija"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Koristi tačnu lokaciju"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kada je tačna lokacija isključena, aplikacije mogu pristupiti vašoj približnoj lokaciji"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Odobrenje za: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Pristup aplikacije za: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Pristup aplikaciji \"<xliff:g id="PERM">%1$s</xliff:g>\" za ovu aplikaciju na uređaju \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Prikaži sva odobrenja aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Prikaži sve aplikacije s ovim odobrenjem"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Prikaži korištenje mikrofona asistenta"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Ukloni odobrenja ako se aplikacija ne koristi"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Ukloni odobrenja i oslobodi prostor"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pauziraj aktivnost apl. ako se ne koristi"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Upravljanje aplikacijom ako se ne koristi"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Uklonite odobrenja, izbrišite privremene fajlove i zaustavite obavještenja"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Uklonite odobrenja, izbrišite privremene fajlove, zaustavite obavještenja i arhivirajte aplikaciju"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Zbog zaštite vaših podataka, odobrenja za ovu aplikaciju će se ukloniti ako se ona ne bude koristila nekoliko mjeseci."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Radi zaštite vaših podataka, ako se aplikacija ne bude koristila nekoliko mjeseci, uklonit će se sljedeća odobrenja: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Zbog zaštite vaših podataka uklonjena su odobrenja iz aplikacija koje niste koristili nekoliko mjeseci."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Dozvoljeno upravljanje svim fajlovima"</string>
<string name="ask_header" msgid="2633816846459944376">"Pitaj svaki put"</string>
<string name="denied_header" msgid="903209608358177654">"Nije dozvoljeno"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> na uređaju \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Pogledajte više aplikacija koje imaju pristup svim fajlovima"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# h}one{# h}few{# h}other{# h}}"</string>
@@ -362,11 +368,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 +380,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikacija za bilješke"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikacije koje vam dozvoljavaju da pravite bilješke na uređaju"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"bilješke"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Zadana aplikacija za novčanik"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikacija za novčanik"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikacije za novčanik mogu pohranjivati kreditne kartice, kartice lojalnosti, ključeve automobila i druge stvari da vam pomognu pri raznim vrstama transakcija."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Postaviti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> kao zadanu aplikaciju za novčanik?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nije potrebno odobrenje"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Trenutno zadano"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ne pitaj ponovo"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Postavi kao zadano"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Više zadanih"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Otvaranje linkova"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Uobičajeno za rad"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Zadano za privatni prostor"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nema"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Sistemski zadano)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nema aplikacija"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Prikaži otkrivanje aktiviranja asistenta"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Prikaz ikone na statusnoj traci kada se mikrofon koristi za aktiviranje glasovne pomoći"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama i medijima na vašem uređaju?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama i medijskim fajlovima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa vašim kontaktima?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa kontaktima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacija će imati pristup lokaciji isključivo dok je koristite"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Moguće je da će aplikacija željeti pristup vašoj lokaciji sve vrijeme, čak i kada je ne budete koristili. "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Promijeniti pristup lokaciji za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Promijeniti pristup lokaciji za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ova aplikacija želi pristup vašoj lokaciji sve vrijeme, čak i kada je ne koristite. "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pronađe uređaje u blizini, poveže se s njima te odredi njihov relativni položaj?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pronalazi uređaje u blizini, povezuje se s njima i utvrđuje njihov relativan položaj?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pronađe uređaje u blizini, poveže se s njima te odredi njihov relativni položaj? "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Promijeniti pristup aplikacije <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> lokaciji iz približne u tačnu?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Promijeniti pristup aplikacije <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> lokaciji na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; iz približne u tačnu?"</string>
<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_device_aware_coarselocation" msgid="8367540370912066757">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa približnoj lokaciji uređaja &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_calendar" msgid="7161929851377463612">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa kalendaru na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_sms" msgid="6639977653040502291">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da šalje i pregleda SMS poruke na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_storage" msgid="6933251810928606636">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama te medijskim i ostalim fajlovima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"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 i drugim fajlovima&lt;/b&gt; na uređaju?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dozvoliti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa muzici i zvuku na ovom uređaju?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa muzici i audio zapisima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama i videozapisima na ovom uređaju?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama i videozapisima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa većem broju fotografija i videozapisa na uređaju?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa dodatnim fotografijama i videozapisima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima zvuk?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima zvuk na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati zvuk samo za vrijeme korištenja"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snimanje zvuka?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima zvuk na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ova aplikacija može tražiti da snima zvuk sve vrijeme, čak i kada je ne koristite. "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Promijeniti pristup mikrofonu za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Promijeniti pristup mikrofonu za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ova aplikacija traži da snima zvuk sve vrijeme, čak i kada je ne koristite. "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa informacijama o vašoj fizičkoj aktivnosti?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fizičkoj aktivnosti na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima fotografije i videozapise?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima slike i videozapise na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija će moći snimati slike i videozapise samo za vrijeme korištenja"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snimanje slika i videozapisa?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da snima slike i videozapise na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ova aplikacija može tražiti da snima slike i videozapise sve vrijeme, čak i kada je ne koristite. "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Promijeniti pristup kameri za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Promijeniti pristup kameri za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ova aplikacija traži da snima slike i videozapise sve vrijeme, čak i kada je ne koristite. "<annotation id="link">"Dozvolite u postavkama."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa zapisnicima poziva?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa zapisnicima poziva na telefonu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da upućuje pozive i upravlja njima?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da upućuje telefonske pozive i njima upravlja na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa senzornim podacima o vašim vitalnim znakovima?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa podacima senzora o vitalnim znakovima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ova aplikacija želi pristupati podacima senzora o vašim vitalnim znakovima sve vrijeme, čak i kada je ne koristite. Da to promijenite, "<annotation id="link">"idite u postavke."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Dozvoliti 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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa podacima senzora o vitalnim znakovima na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Da dozvolite aplikaciji da pristupa podacima tjelesnih senzora sve vrijeme, čak i kada je ne koristite, "<annotation id="link">"idite u postavke."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Nastaviti dozvoljavati aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa podacima tjelesnih senzora dok se koristi?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Nastaviti dozvoljavati aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa podacima tjelesnih senzora na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; dok se koristi?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da vam šalje obavještenja?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Dozvoliti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da vam šalje obavještenja na uređaju &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrolirana odobrenja"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ima pristup lokaciji"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Vaša organizacija dozvoljava aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> da pristupa vašoj lokaciji"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ništa"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Protekla\n24 sata"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Posljednjih\n7 dana"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> posto"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaštićena Androidom. S obzirom na to da se vaši podaci obrađuju na ovom uređaju, korištenje odobrenja ove aplikacije nije prikazano na statusnoj traci ili kontrolnoj tabli za privatnost."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaštićena Androidom. S obzirom na to da se vaši podaci obrađuju na ovom uređaju, korištenje odobrenja ove aplikacije nije prikazano na kontrolnoj tabli za privatnost."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera uređaja je blokirana"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Za aplikacije i usluge"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Podaci mikrofona se i dalje mogu dijeliti kada pozovete broj za hitne slučajeve."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Promijeni"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Pristup kameri je isključen"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Pristup mikrofonu je isključen"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Pristup lokaciji je isključen"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Za informativno-zabavne aplikacije"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Za potrebne aplikacije"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ova aplikacija je potrebna"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ovu aplikaciju zahtijeva proizvođač automobila"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sigurnost i privatnost"</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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Promjene u dijeljenju podataka"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Neke aplikacije mogu promijeniti način na koji mogu dijeliti podatke o lokaciji"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Postavke"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Pristupljeno je u <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pristupljeno je jučer u <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pristupljeno je <xliff:g id="TIME_DATE_0">%1$s</xliff:g> u <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Jednokratna lozinka je 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničena postavka"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Radi vaše sigurnosti postavka trenutno nije dostupna."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je odbijen pristup odobrenju <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup osjetljivom odobrenju, što može ugroziti vaše lične i finansijske informacije.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće ispravno raditi bez ovog ograničenog odobrenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako dozvoliti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je odbijen pristup da bude zadana <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup osjetljivim odobrenjima, što može ugroziti vaše lične i finansijske informacije.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće ispravno raditi bez ovih ograničenih odobrenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako dozvoliti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaciji je odbijen pristup"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup ovom odobrenju može ugroziti vaše lične i finansijske informacije.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće ispravno raditi bez ovog ograničenog odobrenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako dozvoliti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saznajte više"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Uredu"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Zahtjev za odobrenje je potisnut"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikacija traži dodatna odobrenja, ali se ona ne mogu dati u sesiji prijenosa. Najprije dajte odobrenje na telefonu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za pozive ili poruke hitnim službama"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija poslana hitnim službama"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ova je aplikacija pristupila lokaciji vašeg uređaja prilikom upućivanja poziva ili poruke na broj hitne službe. To se može dogoditi čak i kad aplikacija nema dopuštenje za lokaciju ili je lokacija uređaja isključena. "<a href="https://support.google.com/android/answer/9319337">"Saznajte više"</a></string>
</resources>
diff --git a/PermissionController/res/values-ca-v34/strings.xml b/PermissionController/res/values-ca-v34/strings.xml
index 8aad7b657..727208ace 100644
--- a/PermissionController/res/values-ca-v34/strings.xml
+++ b/PermissionController/res/values-ca-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Gestiona l\'accés de les aplicacions a les dades de salut"</string>
<string name="location_settings" msgid="8863940440881290182">"Accés a la ubicació"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Per a aplicacions i serveis. És possible que les dades del micròfon es comparteixin si truques a un número d\'emergència encara que aquesta opció estigui desactivada."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Per a aplicacions i serveis"</string>
</resources>
diff --git a/PermissionController/res/values-ca-watch/strings.xml b/PermissionController/res/values-ca-watch/strings.xml
index a7ba423ff..41bdada45 100644
--- a/PermissionController/res/values-ca-watch/strings.xml
+++ b/PermissionController/res/values-ca-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"No es pot canviar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sí"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancel·la"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"En tot moment"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Quan s\'utilitza l\'aplicació"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"En tot moment"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Quan s\'utilitza l\'aplicació"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"En tot moment"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Quan s\'utilitza l\'aplicació"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"En tot moment"</string>
</resources>
diff --git a/PermissionController/res/values-ca/strings.xml b/PermissionController/res/values-ca/strings.xml
index 0187eabc6..abdd7c39e 100644
--- a/PermissionController/res/values-ca/strings.xml
+++ b/PermissionController/res/values-ca/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string>
<string name="cancel" msgid="8943320028373963831">"Cancel·la"</string>
<string name="back" msgid="6249950659061523680">"Enrere"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Tanca"</string>
<string name="available" msgid="6007778121920339498">"Disponible"</string>
<string name="blocked" msgid="9195547604866033708">"Bloquejat"</string>
<string name="on" msgid="280241003226755921">"Activat"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Tots els permisos"</string>
<string name="other_permissions" msgid="2901186127193849594">"Altres competències de l\'aplicació"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Sol·licitud de permís"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Les accions d\'instal·lar o de desinstal·lar no s\'admeten a Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Tria a què vols que tingui accés &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"S\'ha actualitzat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Tria a què vols que tingui accés aquesta aplicació."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancel·la"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Permet sempre tot"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Pregunta sempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"No permetis"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permet l\'accés limitat"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Ubicació precisa"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Ubicació aproximada"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Utilitza la ubicació precisa"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Quan la ubicació precisa està desactivada, les aplicacions poden accedir a la teva ubicació aproximada"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Permís d\'accés a <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Accés a <xliff:g id="PERM">%1$s</xliff:g> per a aquesta aplicació"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Accés de <xliff:g id="PERM">%1$s</xliff:g> a aquesta aplicació (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Mostra tots els permisos per a <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Mostra totes les aplicacions que tenen aquest permís"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostra l\'ús del micròfon de l\'Assistent"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Suprimeix els permisos si no s\'utilitza l\'aplicació"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Suprimeix els permisos i allibera espai"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Activitat a l\'app en pausa si no s\'usa"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gestiona l\'aplicació si no s\'utilitza"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Suprimeix els permisos i els fitxers temporals, i atura les notificacions"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Suprimeix els permisos, elimina els fitxers temporals, atura les notificacions i arxiva l\'aplicació"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Per protegir les teves dades, els permisos d\'aquesta aplicació se suprimiran si no la utilitzes durant uns mesos."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Si l\'aplicació no s\'utilitza durant uns mesos, se suprimiran els permisos següents per protegir les teves dades: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Per protegir les teves dades, s\'han suprimit els permisos de les aplicacions que no has utilitzat durant els darrers mesos."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Poden gestionar tots els fitxers"</string>
<string name="ask_header" msgid="2633816846459944376">"Pregunta sempre"</string>
<string name="denied_header" msgid="903209608358177654">"Sense permís"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Mostra més aplicacions que poden accedir a tots els fitxers"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}many{# dies}other{# dies}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# hores}other{# hores}}"</string>
@@ -290,7 +296,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 +361,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 +369,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,10 +403,15 @@
<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>
+ <string name="role_wallet_label" msgid="3719419175656204207">"App de cartera predeterminada"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplicació de cartera"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Les aplicacions de cartera poden emmagatzemar les targetes de crèdit i de fidelització, les claus del cotxe i altres elements per ajudar-te amb diverses formes de transaccions."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Vols definir <xliff:g id="APP_NAME">%1$s</xliff:g> com l\'aplicació de cartera predeterminada?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No calen permisos"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Predeterminada actualment"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"No m\'ho tornis a preguntar"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Estableix com a predeterminada"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Més aplicacions predeterminades"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Obertura d\'enllaços"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predeterminada per a la feina"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Predeterminades per a l\'espai privat"</string>
<string name="default_app_none" msgid="9084592086808194457">"Cap"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Opció predeterminada del sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Cap aplicació"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostra la detecció d\'activador de l\'assistent"</string>
<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_device_aware_storage_isolated" msgid="6463062962458809752">"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 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_contacts" msgid="731025863972535928">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als contactes del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Vols canviar l\'accés a la ubicació per a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Vols canviar l\'accés a la ubicació per a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"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>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; cerqui els dispositius propers, s\'hi connecti i en determini la posició relativa?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; cerqui la posició relativa de dispositius propers, la determini i s\'hi connecti al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; cerqui els dispositius propers, s\'hi connecti i en determini la posició relativa? "<annotation id="link">"Permet a Configuració"</annotation>"."</string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Vols canviar l\'accés a la ubicació que té <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> d\'aproximada a exacta?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Vols canviar l\'accés a la ubicació de <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; d\'Aproximada a Precisa?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació aproximada del dispositiu?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la ubicació aproximada del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exacta"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aproximada"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi al calendari?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi al calendari del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; enviï i llegeixi missatges SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; enviï i consulti missatges SMS al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a fotos, contingut multimèdia i fitxers del dispositiu?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les fotos, al contingut multimèdia i als fitxers del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als &lt;b&gt;vídeos, fotos, música i àudio&lt;/b&gt; del dispositiu?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als &lt;b&gt;vídeos, fotos, música, àudio i altres fitxers&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la música i l\'àudio d\'aquest dispositiu?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la música i l\'àudio del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les fotos i els vídeos d\'aquest dispositiu?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les fotos i als vídeos del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a més fotos i vídeos d\'aquest dispositiu?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a més fotos i vídeos al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'aplicació només podrà gravar àudio mentre l\'estiguis utilitzant"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gravi àudio al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"És possible que aquesta aplicació vulgui gravar àudio sempre, fins i tot quan no l\'estiguis utilitzant. "<annotation id="link">"Pots permetre-ho a la configuració"</annotation>"."</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Vols canviar l\'accés al micròfon de l\'aplicació &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Vols canviar l\'accés al micròfon per a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Aquesta aplicació vol gravar àudio sempre, fins i tot quan no l\'estiguis utilitzant. "<annotation id="link">"Pots permetre-ho a la configuració"</annotation>"."</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la teva activitat física?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a la teva activitat física del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci fotos i gravi vídeos?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci fotos i gravi vídeos al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"L\'aplicació només podrà fer fotos i gravar vídeos mentre l\'estiguis utilitzant"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci fotos i gravi vídeos?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci fotos i gravi vídeos al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"És possible que aquesta app vulgui fer fotos i gravar vídeos sempre, fins i tot quan no l\'estiguis utilitzant. "<annotation id="link">"Pots permetre-ho a la configuració"</annotation>"."</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Vols canviar l\'accés a la càmera de l\'aplicació &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Vols canviar l\'accés a la càmera per a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Aquesta aplicació vol fer fotos i gravar vídeos sempre, fins i tot quan no l\'estiguis utilitzant. "<annotation id="link">"Pots permetre-ho a la configuració"</annotation>"."</string>
<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_device_aware_calllog" msgid="8220927190376843309">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi als registres de trucades del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faci i gestioni trucades amb el dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les dades del sensor sobre les constants vitals al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; accedeixi a les dades del sensor sobre les constants vitals al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"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 del dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; mentre s\'utilitza l\'aplicació?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; t\'enviï notificacions?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Vols permetre que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; t\'enviï notificacions al dispositiu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Permisos controlats"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> té accés a la ubicació"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"La teva organització permet a <xliff:g id="APP_NAME">%1$s</xliff:g> accedir a la teva ubicació"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Cap"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 darreres\nhores"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7\ndarrers dies"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> per cent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> està protegida per Android. Com que les teves dades es tracten en aquest dispositiu, l\'ús del permís d\'aquesta aplicació no es mostra a la barra d\'estat ni al tauler de privadesa."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> està protegida per Android. Com que les teves dades es tracten en aquest dispositiu, l\'ús del permís d\'aquesta aplicació no es mostra al tauler de privadesa."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"La càmera del dispositiu està bloquejada"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Per a aplicacions i serveis"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"És possible que les dades del micròfon es comparteixin si truques a un número d\'emergència."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Canvia"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"L\'accés a Càmera està desactivat"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"L\'accés al micròfon està desactivat"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"L\'accés a la ubicació està desactivat"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Per a les aplicacions d\'informació i entreteniment"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Per a les aplicacions requerides"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Es requereix aquesta aplicació"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"El fabricant del cotxe requereix aquesta aplicació"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Seguretat i privadesa"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analitza el dispositiu"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ignora"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Canvis en la compartició de dades"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Algunes aplicacions han canviat la manera en què poden compartir les teves dades d\'ubicació"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Configuració"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"S\'hi ha accedit a les <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"S\'hi va accedir ahir a les <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"S\'hi ha accedit el dia <xliff:g id="TIME_DATE_0">%1$s</xliff:g> a les <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"La contrasenya d\'un sol ús és 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Opció de configuració restringida"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Per a la teva seguretat, aquesta opció de configuració no està disponible en aquests moments."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"A l\'aplicació se li ha denegat l\'accés a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'aplicació ha demanat accés a un permís sensible, el qual pot posar en risc la teva informació personal o financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís restringit. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"A l\'aplicació se li ha denegat l\'accés per ser <xliff:g id="ROLE_NAME">%1$s</xliff:g> de manera predeterminada"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"L\'aplicació ha demanat accés a permisos sensibles, els quals poden posar en risc la teva informació personal o financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquests permisos restringits. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"A l\'aplicació se li ha denegat l\'accés"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accés a aquest permís pot posar en risc la teva informació personal i financera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>És possible que l\'aplicació no funcioni correctament sense aquest permís. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Més informació sobre com pots permetre l\'accés&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Més informació"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"D\'acord"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"S\'ha suprimit la sol·licitud de permís"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aquesta aplicació requereix permisos addicionals, però els permisos no es poden concedir en una sessió d\'estríming. Primer concedeix el permís al teu telèfon."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Per a trucades o missatges de text d\'emergència"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"La ubicació s\'ha enviat als serveis d\'emergències"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Aquesta aplicació ha accedit a la ubicació del teu dispositiu mentre feies una trucada o enviaves un missatge de text a un número d\'emergència. Això pot passar fins i tot quan l\'aplicació no té permís d\'ubicació o la ubicació del dispositiu està desactivada. "<a href="https://support.google.com/android/answer/9319337">"Més informació"</a></string>
</resources>
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-v34/strings.xml b/PermissionController/res/values-cs-v34/strings.xml
index b7e982b21..dd60a422d 100644
--- a/PermissionController/res/values-cs-v34/strings.xml
+++ b/PermissionController/res/values-cs-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Správa přístupu aplikací ke zdravotním údajům"</string>
<string name="location_settings" msgid="8863940440881290182">"Přístup k poloze"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Aplikace a služby. Pokud je toto nastavení vypnuté a zavoláte na číslo tísňového volání, data z mikrofonu bude možné nadále sdílet"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Aplikace a služby"</string>
</resources>
diff --git a/PermissionController/res/values-cs-watch/strings.xml b/PermissionController/res/values-cs-watch/strings.xml
index eca4682c4..5e1a4b022 100644
--- a/PermissionController/res/values-cs-watch/strings.xml
+++ b/PermissionController/res/values-cs-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nelze změnit"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ano"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Zrušit"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Neustále"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Při používání aplikace"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Neustále"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Při používání aplikace"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Neustále"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Při používání aplikace"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Neustále"</string>
</resources>
diff --git a/PermissionController/res/values-cs/strings.xml b/PermissionController/res/values-cs/strings.xml
index 1c28eb912..51fd20c65 100644
--- a/PermissionController/res/values-cs/strings.xml
+++ b/PermissionController/res/values-cs/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"oprávnění"</string>
<string name="cancel" msgid="8943320028373963831">"Zrušit"</string>
<string name="back" msgid="6249950659061523680">"Zpět"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Zavřít"</string>
<string name="available" msgid="6007778121920339498">"Dostupné"</string>
<string name="blocked" msgid="9195547604866033708">"Blokováno"</string>
<string name="on" msgid="280241003226755921">"Zapnuto"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Všechna oprávnění"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ostatní oprávnění aplikace"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Žádost o oprávnění"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Akce instalace/odinstalace nejsou v zařízení Wear podporovány."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Určete, k čemu aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; povolíte přístup"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; byla aktualizována. Určete, k čemu jí povolíte přístup."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Zrušit"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Vždy povolit vše"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Pokaždé se zeptat"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nepovolovat"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Povolit omezený přístup"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Přesná poloha"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Přibližná poloha"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Používat přesnou polohu"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Když je přesná poloha vypnutá, aplikace mají přístup k vaší přibližné poloze"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Oprávnění: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Přístup této aplikace k oprávnění: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Přístup k <xliff:g id="PERM">%1$s</xliff:g> pro tuto aplikaci v zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Zobrazit všechna oprávnění aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Zobrazit všechny aplikace s tímto oprávněním"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Zobrazit používání mikrofonu asistentem"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Odebrat oprávnění, pokud se aplikace nepoužívá"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Odebrat oprávnění a uvolnit místo"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pozastavit aktivitu při nepoužívání"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Správa nepoužívané aplikace"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Odebrat oprávnění, smazat dočasné soubory a zastavit oznámení"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Odebrat oprávnění, smazat dočasné soubory, zastavit oznámení a archivovat aplikaci"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Pokud tuto aplikaci několik měsíců nepoužijete, kvůli ochraně vašich dat jí budou oprávnění odebrána."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Pokud tuto aplikaci několik měsíců nepoužijete, budou jí kvůli ochraně vašich dat odebrána následující oprávnění: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Za účelem ochrany vašich dat byla odebrána oprávnění aplikacím, které jste několik měsíců nepoužili."</string>
@@ -219,7 +224,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Povolena správa všech souborů"</string>
<string name="ask_header" msgid="2633816846459944376">"Pokaždé se zeptat"</string>
<string name="denied_header" msgid="903209608358177654">"Nepovoleno"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> na <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Zobrazit další aplikace s přístupem ke všem souborům"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 den}few{# dny}many{# dne}other{# dní}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hodinu}few{# hodiny}many{# hodiny}other{# hodin}}"</string>
@@ -375,7 +381,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikace pro poznámky"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikace, které umožňují dělat si na zařízení poznámky."</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"poznámky"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Výchozí aplikace typu peněženka"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikace typu peněženka"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikace typu peněženka můžou uložit vaše platební a věrnostní karty, klíče od auta a další věci, a usnadnit vám tak různé transakce."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Nastavit aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> jako výchozí peněženku?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nejsou potřeba žádná oprávnění"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Aktuálně výchozí"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Příště se neptat"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Změnit na výchozí"</string>
@@ -426,7 +437,8 @@
<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_apps_for_private_profile" msgid="2022024112144880785">"Výchozí pro soukromý prostor"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Zobrazit detekci spuštění asistenta"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Zobrazovat na stavovém řádku ikonu, když bude pomocí mikrofonu aktivován hlasový asistent"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám a mediálnímu obsahu v zařízení?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám a médiím v zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup ke kontaktům?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k vašim kontaktům na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k poloze tohoto zařízení?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k poloze zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikace bude mít přístup k poloze, pouze když ji budete používat"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k poloze tohoto zařízení?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k poloze zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Aplikace může požadovat přístup k poloze vždy, i když ji nebudete používat. "<annotation id="link">"Povolit ho můžete v nastavení"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Změnit přístup k poloze pro aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Změnit na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; k poloze?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Aplikace požaduje přístup k poloze vždy, i když ji nebudete používat. "<annotation id="link">"Povolit ho můžete v nastavení"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; najít zařízení v okolí, připojit se k nim a zjistit jejich relativní polohu?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; najít zařízení v okolí, připojit se k nim a zjistit jejich relativní polohu?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; najít zařízení v okolí, připojit se k nim a zjistit jejich relativní polohu? "<annotation id="link">"Povolit to můžete v nastavení."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Změnit přístup aplikace <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> k poloze z přibližné na přesnou?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Změnit na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup aplikace <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> k poloze z přibližné na přesnou?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k přibližné poloze tohoto zařízení?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k přibližné poloze zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Přesná"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Přibližná"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup ke kalendáři?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k vašemu kalendáři na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; odesílat a zobrazovat SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; odesílat a zobrazovat SMS?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám, mediálnímu obsahu a souborům v zařízení?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám, médiím a videím v zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k &lt;b&gt;fotkám, videím, hudbě a zvuku&lt;/b&gt; v zařízení?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k &lt;b&gt;fotkám, videím, hudbě, zvuku a dalším souborům&lt;/b&gt; v zařízení?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k hudbě a zvuku v zařízení?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k hudbě a zvukovému obsahu v zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám a videím v zařízení?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k fotkám a videím v zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k dalším fotkám a videím v zařízení?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k dalším fotkám a videím v zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?​"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat zvuk?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; zvuk?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikace bude moci zaznamenávat zvuk, pouze když ji budete používat"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat zvuk?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávat na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; zvuk?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Tato aplikace může chtít zaznamenávat zvuk kdykoli, dokonce i když ji nepoužíváte. "<annotation id="link">"Povolit v nastavení"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Změnit přístup k mikrofonu pro aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Změnit na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; k mikrofonu?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Tato aplikace chce zaznamenávat zvuk kdykoli, dokonce i když ji nepoužíváte. "<annotation id="link">"Povolit v nastavení"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k vaší fyzické aktivitě?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup k vaší fyzické aktivitě?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotit a nahrávat videa?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotit na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; a nahrávat na něm video?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikace bude moci pořizovat snímky a nahrávat videa, pouze když ji budete používat"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotit a nahrávat videa?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotit na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; a nahrávat na něm video?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Tato aplikace může chtít pořizovat snímky a nahrávat videa kdykoli, dokonce i když ji nepoužíváte. "<annotation id="link">"Povolit v nastavení"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Změnit přístup k fotoaparátu pro aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Změnit na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; k mikrofonu?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Tato aplikace chce pořizovat snímky a nahrávat videa kdykoli, dokonce i když ji nepoužíváte. "<annotation id="link">"Povolit v nastavení"</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k seznamu telefonních hovorů?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k vašim seznamům hovorů na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uskutečňovat a spravovat telefonní hovory?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uskutečňovat a spravovat na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; telefonní hovory?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k údajům ze senzorů vašich životních funkcí?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup k datům ze senzorů životních funkcí?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplikace požaduje přístup k datům ze senzorů vašich životních funkcí vždy, i když ji nebudete používat. Pokud tuto změnu chcete provést, "<annotation id="link">"přejděte do nastavení."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; přístup k datům ze senzorů vašich životních funkcí?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; přístup k datům ze senzorů životních funkcí?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Pokud chcete této aplikaci povolit trvalý přístup k datům z tělesných senzorů, i když aplikaci nepoužíváte, "<annotation id="link">"přejděte do nastavení"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Chcete, aby aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dál měla přístup k datům z tělesných senzorů během používání aplikace?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Chcete, aby aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; měla během používání i nadále přístup k datům z tělesných senzorů?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; odesílat oznámení?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Povolit aplikaci &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; odesílat vám na zařízení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; oznámení?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Spravovaná oprávnění"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> má přístup k poloze"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Vaše organizace umožňuje aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> přístup k vaší poloze"</string>
@@ -512,6 +551,9 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Žádné"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Posledních\n24 hodin"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Posledních\n7 dní"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for privdash_usage_percent (6893824766124414127) -->
+ <skip />
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je chráněna Androidem. Protože se vaše data zpracovávají na tomto zařízení, oprávnění používaná touto aplikací se nezobrazují na stavovém řádku ani na panelu ochrany soukromí."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je chráněna Androidem. Protože se vaše data zpracovávají na tomto zařízení, oprávnění používaná touto aplikací se nezobrazují na panelu ochrany soukromí."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Fotoaparát zařízení je blokován"</string>
@@ -520,6 +562,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Pro aplikace a služby"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Když zavoláte na číslo tísňového volání, mohou být nadále sdílena data z mikrofonu."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Změnit"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Přístup k fotoaparátu je vypnutý"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Přístup k mikrofonu je vypnutý"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Přístup k poloze je vypnutý."</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Pro aplikace informační a zábavní aplikace"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Pro povinné aplikace"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Tato aplikace je povinná"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Tuto aplikaci výrobce auta vyžaduje"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Zabezpečení a ochrana soukromí"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Zkontrolovat zařízení"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Zavřít"</string>
@@ -581,7 +630,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>
@@ -619,4 +668,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Aktualizace sdílení dat"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Některé aplikace změnily způsob, kterým mohou sdílet údaje o vaší poloze"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Nastavení"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Poslední přístup: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Poslední přístup: včera v <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Poslední přístup: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Vaše jednorázové heslo je 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Blokované nastavení"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Z důvodu vaší bezpečnosti toto nastavení momentálně není dostupné."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaci byl odepřen přístup k oprávnění <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikace požádala o přístup k citlivému oprávnění, které může ohrozit vaše osobní a finanční údaje.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že aplikace bez tohoto oprávnění nebude fungovat správně. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak povolit přístup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaci byla odepřena role výchozí <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikace požádala o přístup k citlivým oprávněním, která mohou ohrozit vaše osobní a finanční údaje.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že aplikace nebude bez těchto omezených oprávnění fungovat správně. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak povolit přístup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaci byl odepřen přístup"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Přístup k tomuto oprávnění může ohrozit vaše osobní a finanční údaje.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že aplikace bez tohoto oprávnění nebude fungovat správně. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak povolit přístup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Další informace"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Žádost o oprávnění byla potlačena"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Tato aplikace požaduje další oprávnění, která ale nelze udělit během relace streamování. Oprávnění udělte nejprve na telefonu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pro tísňové volání nebo textovou zprávu"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Poloha byla odeslána na tísňovou linku"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Tato aplikace získala přístup k poloze vašeho zařízení během hovoru nebo při posílání SMS na číslo tísňového volání. K tomu může dojít, i když aplikace nemá oprávnění pro přístup k poloze nebo je poloha zařízení vypnutá. "<a href="https://support.google.com/android/answer/9319337">"Další informace"</a></string>
</resources>
diff --git a/PermissionController/res/values-da-v34/strings.xml b/PermissionController/res/values-da-v34/strings.xml
index 5777a6832..38dc3c7b8 100644
--- a/PermissionController/res/values-da-v34/strings.xml
+++ b/PermissionController/res/values-da-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Administrer appadgang til sundhedsdata"</string>
<string name="location_settings" msgid="8863940440881290182">"Lokationsadgang"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"For apps og tjenester. Hvis denne indstilling er deaktiveret, deles mikrofondata muligvis stadig, når du ringer til et alarmnummer"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"For apps og tjenester"</string>
</resources>
diff --git a/PermissionController/res/values-da-watch/strings.xml b/PermissionController/res/values-da-watch/strings.xml
index 022b2f562..d78c45ff4 100644
--- a/PermissionController/res/values-da-watch/strings.xml
+++ b/PermissionController/res/values-da-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Kan ikke ændres"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ja"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Annuller"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Hele tiden"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Når appen er i brug"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Hele tiden"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Når appen er i brug"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Hele tiden"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Når appen er i brug"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Hele tiden"</string>
</resources>
diff --git a/PermissionController/res/values-da/strings.xml b/PermissionController/res/values-da/strings.xml
index 71e82343c..f557db8ab 100644
--- a/PermissionController/res/values-da/strings.xml
+++ b/PermissionController/res/values-da/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"tilladelser"</string>
<string name="cancel" msgid="8943320028373963831">"Annuller"</string>
<string name="back" msgid="6249950659061523680">"Tilbage"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Luk"</string>
<string name="available" msgid="6007778121920339498">"Tilgængelig"</string>
<string name="blocked" msgid="9195547604866033708">"Blokeret"</string>
<string name="on" msgid="280241003226755921">"Til"</string>
@@ -34,6 +35,7 @@
<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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Alle tilladelser"</string>
<string name="other_permissions" msgid="2901186127193849594">"Andre app-egenskaber"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Anmodning om tilladelse"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Det er ikke muligt at installere/afinstallere på Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Vælg, hvad &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; må få adgang til"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; er blevet opdateret. Vælg, hvad denne app må få adgang til."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Annuller"</string>
@@ -181,8 +182,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>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Tillad altid alle"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Spørg hver gang"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Tillad ikke"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Tillad begrænset adgang"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Præcis lokation"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Omtrentlig lokation"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Brug præcis lokation"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Når den præcise lokation er slået fra, kan apps få adgang til din omtrentlige lokation"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Tilladelse til <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Adgang til <xliff:g id="PERM">%1$s</xliff:g> for denne app"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"adgang til <xliff:g id="PERM">%1$s</xliff:g> for denne app på <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Se alle tilladelser for <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Se alle apps med denne tilladelse"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Vis brug af Assistent-mikrofonen"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Fjern tilladelser, hvis appen ikke bruges"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Fjern tilladelser, og frigør plads"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Sæt appaktivitet på pause ved inaktivitet"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Administrer appen, hvis den ikke bruges"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Fjern tilladelser, slet midlertidige filer, og stop notifikationer"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Fjern tilladelser, slet midlertidige filer, deaktiver notifikationer, og arkivér appen"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Hvis appen ikke bliver brugt i et par måneder, fjernes tilladelser for appen for at beskytte dine data."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Hvis appen ikke bliver brugt i et par måneder, fjernes følgende tilladelser for at beskytte dine data: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Tilladelserne til apps, du ikke har brugt i et par måneder, er blevet fjernet for at beskytte dine data."</string>
@@ -221,9 +226,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 +241,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Har tilladelse til at administrere alle filer"</string>
<string name="ask_header" msgid="2633816846459944376">"Spørg hver gang"</string>
<string name="denied_header" msgid="903209608358177654">"Ikke tilladt"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> på <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Se flere apps, som kan tilgå alle filer"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}one{# dag}other{# dage}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# time}one{# time}other{# timer}}"</string>
@@ -315,24 +321,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>
@@ -362,7 +368,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"App til notetagning"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps, som giver dig mulighed for at tage noter på din enhed"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"noter"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Standardapp til digital pung"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"App til digital pung"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Apps til digitale punge kan gemme dine kredit- og loyalitetskort, bilnøgler m.m. for at hjælpe med forskellige former for transaktioner."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Vil du angive <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardapp til digital pung?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Der kræves ingen tilladelser"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Nuværende standardapp"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Spørg ikke igen"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Angiv som standard"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Flere standardapps"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Åbning af links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Standard til arbejde"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Standard for privat område"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ingen"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Systemstandard)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Ingen apps"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Vis aktiveringsregistrering for assistenten"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Vis ikon på statusbjælken, når mikrofonen bruges til at aktivere taleassistenten"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder og medier på din enhed?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder og medier på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til dine kontakter?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til dine kontakter på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til enhedens lokation?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til lokationen på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Appen har kun adgang til lokationen, når du bruger appen"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til enhedens lokation?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til lokationen på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Denne app vil muligvis gerne have adgang til din lokation hele tiden, også når du ikke bruger appen. "<annotation id="link">"Giv tilladelse under Indstillinger."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Vil du skifte lokationsadgang for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Vil du ændre lokationsadgangen for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Appen vil gerne have adgang til din lokation hele tiden, også når du ikke bruger appen. "<annotation id="link">"Giv appen tilladelse i Indstillinger."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Skal &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kunne finde, oprette forbindelse til og fastslå den relative placering af enheder i nærheden?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at finde, oprette forbindelse til og bestemme den relative position af enheder i nærheden på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Skal &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kunne finde, oprette forbindelse til og fastslå den relative placering af enheder i nærheden? "<annotation id="link">"Tillad i Indstillinger."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Vil du skifte lokationsadgang for <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> fra omtrentlig til nøjagtig?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Vil du ændre lokationsadgangen for <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; fra omtrentlig til præcis?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til enhedens omtrentlige lokation?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til den omtrentlige lokation på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Præcis"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Omtrentlig"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til din kalender?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til din kalender på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at sende og se sms-beskeder?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at sende og se sms-beskeder på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder, medier og filer på din enhed?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder, medier og filer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til &lt;b&gt;billeder, videoer, musik og lyd&lt;/b&gt; på denne enhed?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til &lt;b&gt;billeder, videoer, musik, lyd og andre filer&lt;/b&gt; på denne enhed?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til musik og lyd på denne enhed?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til musik og lyd på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder og videoer på denne enhed?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til billeder og videoer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at tilgå flere billeder og videoer på denne enhed?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til flere billeder og videoer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan kun optage lyd, mens du bruger appen"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at optage lyd på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Denne app vil gerne optage lyd hele tiden, også når du ikke bruger appen. "<annotation id="link">"Tillad dette i indstillingerne"</annotation>"."</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Vil du skifte mikrofonadgang for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Vil du ændre mikrofonadgangen for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Denne app vil gerne optage lyd hele tiden, også når du ikke bruger appen. "<annotation id="link">"Tillad dette i indstillingerne"</annotation>"."</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Vil du tillade, at &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; får adgang til din fysiske aktivitet?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til din fysiske aktivitet på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at tage billeder og optage video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at tage billeder og optage video på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Appen kan kun tage billeder og optage video, mens du bruger appen"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at tage billeder og optage video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at tage billeder og optage video på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Denne app vil gerne tage billeder og optage video hele tiden, også når du ikke bruger appen. "<annotation id="link">"Tillad dette i indstillingerne"</annotation>"."</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Vil du skifte kameraadgang for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Vil du ændre kameraadgangen for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Denne app vil gerne tage billeder og optage video hele tiden, også når du ikke bruger appen. "<annotation id="link">"Tillad dette i indstillingerne"</annotation>"."</string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til dine opkaldslister?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til din opkaldshistorik på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at foretage og administrere telefonopkald?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at foretage og håndtere telefonopkald på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til sensordata om dine vitale værdier?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til sensordata om dine vitale værdier på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Denne app anmoder om at tilgå sensordataene om dine vitale værdier hele tiden, også når du ikke bruger appen. Du kan foretage denne ændring ved at "<annotation id="link">"gå til indstillingerne."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til sensordataene om dine vitale værdier?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til sensordataene om dine vitale værdier på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Du kan give denne app adgang til kropssensordata hele tiden – selv når du ikke bruger appen – ved at "<annotation id="link">"gå til indstillingerne."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Vil du fortsætte med at give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til kropssensordata, mens appen er i brug?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Vil du fortsætte med at give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; adgang til kropssensordata på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;, mens appen er i brug?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at sende dig notifikationer?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Vil du give &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilladelse til at sende dig notifikationer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Administrerede tilladelser"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> har lokationsadgang"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Din organisation har givet <xliff:g id="APP_NAME">%1$s</xliff:g> tilladelse til at tilgå din lokation"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ingen"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Seneste\n24 timer"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"De seneste\n7 dage"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> procent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> beskyttes af Android. Da dine data behandles på denne enhed, vises denne apps anvendelse af tilladelser ikke i statusbjælken i dit privatlivspanel."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> beskyttes af Android. Da dine data behandles på denne enhed, vises denne apps anvendelse af tilladelser ikke i dit privatlivspanel."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Enhedens kamera er blokeret"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"For apps og tjenester"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofondata deles muligvis stadig, når du ringer til et alarmnummer."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Skift"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kameraadgang er deaktiveret"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofonadgang er deaktiveret"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Placeringsadgang er deaktiveret"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Til infotainmentapps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Til påkrævede apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Denne app er påkrævet"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Bilproducenten kræver denne app"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sikkerhed og privatliv"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Scan enhed"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Afvis"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Opdateringer om datadeling"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Nogle apps har ændret måden, hvorpå de kan dele dine lokationsdata"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Indstillinger"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Tilgået kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Tilgået i går kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Tilgået <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Din engangskode er 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begrænset indstilling"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Af hensyn til din sikkerhed er denne indstilling i øjeblikket ikke tilgængelig."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appen blev nægtet adgang til <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen anmodede om adgang til en følsom tilladelse, der kan kompromittere dine personlige eller økonomiske oplysninger.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen fungerer muligvis ikke korrekt uden denne begrænsede tilladelse. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Se, hvordan du giver tilladelse&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen blev nægtet adgang til at være <xliff:g id="ROLE_NAME">%1$s</xliff:g> som standard"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Appen anmodede om adgang til en følsom tilladelse, der kan kompromittere dine personlige eller økonomiske oplysninger.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen fungerer muligvis ikke korrekt uden denne begrænsede tilladelse. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Se, hvordan du giver tilladelse&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Appen blev nægtet adgang"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Hvis du giver adgang til denne tilladelse, kan dine personlige og økonomiske oplysninger kompromitteres.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen fungerer muligvis ikke korrekt uden denne begrænsede tilladelse. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Se, hvordan du giver tilladelse&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Få flere oplysninger"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Anmodning om tilladelse blokeret"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Denne app anmoder om yderligere tilladelser, men der kan ikke gives tilladelser under en streamingsession. Giv tilladelse på din telefon først."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For opkald eller beskeder til et alarmnummer"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokation sendt til nødtjenester"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Denne app har tilgået din enheds lokation i forbindelse med et opkald eller en besked til et alarmnummer. Dette kan lade sig gøre, selvom appen ikke har lokationstilladelse, eller enhedens lokation er deaktiveret. "<a href="https://support.google.com/android/answer/9319337">"Få flere oplysninger"</a></string>
</resources>
diff --git a/PermissionController/res/values-de-v34/strings.xml b/PermissionController/res/values-de-v34/strings.xml
index ed699cce3..f85972421 100644
--- a/PermissionController/res/values-de-v34/strings.xml
+++ b/PermissionController/res/values-de-v34/strings.xml
@@ -22,6 +22,5 @@
<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="location_settings_subtitle" msgid="6846532794702613851">"Für Apps und Dienste"</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>
</resources>
diff --git a/PermissionController/res/values-de-watch/strings.xml b/PermissionController/res/values-de-watch/strings.xml
index 617413a19..dbe09e316 100644
--- a/PermissionController/res/values-de-watch/strings.xml
+++ b/PermissionController/res/values-de-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Änderung unmöglich"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ja"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Abbrechen"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Immer"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Beim Verwenden der App"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Immer"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Beim Verwenden der App"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Immer"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Beim Verwenden der App"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Immer"</string>
</resources>
diff --git a/PermissionController/res/values-de/strings.xml b/PermissionController/res/values-de/strings.xml
index 9e29e51d9..749966dcf 100644
--- a/PermissionController/res/values-de/strings.xml
+++ b/PermissionController/res/values-de/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"Berechtigungen"</string>
<string name="cancel" msgid="8943320028373963831">"Abbrechen"</string>
<string name="back" msgid="6249950659061523680">"Zurück"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Schließen"</string>
<string name="available" msgid="6007778121920339498">"Verfügbar"</string>
<string name="blocked" msgid="9195547604866033708">"Gesperrt"</string>
<string name="on" msgid="280241003226755921">"An"</string>
@@ -32,8 +33,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 +62,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 +73,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Alle Berechtigungen"</string>
<string name="other_permissions" msgid="2901186127193849594">"Andere App-Funktionen"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Berechtigungsanfrage"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Installations-/Deinstallationsaktion auf Android Wear nicht unterstützt."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Worauf darf die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; zugreifen?"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; wurde aktualisiert. Worauf darf diese App zugreifen?"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Abbrechen"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Immer vollen Zugriff erlauben"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Jedes Mal fragen"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nicht zulassen"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Eingeschränkten Zugriff zulassen"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Auf genauen Standort zugreifen"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Ungefährer Standort"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Genauen Standort verwenden"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Wenn der genaue Standort deaktiviert ist, können Apps auf deinen ungefähren Standort zugreifen"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Berechtigung: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Dieser App Zugriff auf <xliff:g id="PERM">%1$s</xliff:g> erlauben?"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g>-Zugriff für diese App auf dem <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Alle Berechtigungen der App „<xliff:g id="APP">%1$s</xliff:g>“ anzeigen"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Alle Apps mit dieser Berechtigung anzeigen"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Nutzung der Berechtigung \"Mikrofon\" für Assistant anzeigen"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Berechtigungen entfernen, wenn die App nicht verwendet wird"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Berechtigungen löschen und Speicherplatz freigeben"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"App-Aktivität bei Nichtnutzung stoppen"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"App-Verhalten bei Nichtnutzung verwalten"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Berechtigungen entfernen, temporäre Dateien löschen und Benachrichtigungen stoppen"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Berechtigungen entfernen, temporäre Dateien löschen, Benachrichtigungen stoppen und die App archivieren"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Zum Schutz deiner Daten werden dieser App die Berechtigungen entzogen, wenn du sie einige Monate nicht verwendest."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Zum Schutz deiner Daten werden dieser App die folgenden Berechtigungen entzogen, wenn du sie einige Monate nicht verwendest: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Zum Schutz deiner Daten wurden Apps, die du einige Monate nicht verwendet hast, Berechtigungen entzogen."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Verwalten aller Dateien zugelassen"</string>
<string name="ask_header" msgid="2633816846459944376">"Jedes Mal fragen"</string>
<string name="denied_header" msgid="903209608358177654">"Nicht zugelassen"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> auf dem <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Weitere Apps anzeigen, die auf alle Dateien zugreifen können"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 Tag}other{# Tage}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# Stunde}other{# Stunden}}"</string>
@@ -363,7 +369,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notizen-App"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps zum Erstellen von Notizen auf deinem Gerät"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"Notizen"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Standard-Wallet-App"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet-App"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"In Wallet-Apps kannst du deine Kredit- und Kundenkarten, Autoschlüssel und andere Dinge speichern, was dir bei verschiedenen Arten von Transaktionen hilft."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> als Standard-Wallet-App festlegen?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Keine Berechtigungen erforderlich"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Aktueller Standard"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Nicht mehr fragen"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Als Standard festlegen"</string>
@@ -426,8 +437,9 @@
<string name="default_apps_more" msgid="4078194675848858093">"Weitere Standard-Apps"</string>
<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_apps_for_private_profile" msgid="2022024112144880785">"Standard-Apps für das vertrauliche Profil"</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,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf Fotos und Medien zugreifen?"</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_device_aware_contacts" msgid="731025863972535928">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf deine Kontakte zugreifen?"</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="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf den Standort deines Geräts &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; zugreifen?"</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>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf den Standort deines Geräts &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; zugreifen?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Diese App möchte eventuell Zugriff auf deinen Standort haben, auch wenn du sie nicht verwendest. "<annotation id="link">"Du kannst das in den Einstellungen zulassen."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Standortzugriff für &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ändern?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Standortzugriff für &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ändern?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Diese App möchte jederzeit Zugriff auf deinen Standort haben, auch wenn du sie nicht verwendest. "<annotation id="link">"Du kannst das in den Einstellungen zulassen."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; Geräte in der Nähe finden, sich mit ihnen verbinden und ihre relative Position bestimmen kann?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; die relative Position von Geräten in der Nähe bestimmen und sich verbinden?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Zulassen, dass &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; Geräte in der Nähe finden, sich mit ihnen verbinden und ihre relative Position bestimmen kann? "<annotation id="link">"In den Einstellungen zulassen."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Soll der Standortzugriff von <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> von „Ungefähr“ zu „Genau“ geändert werden?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Standortzugriff von &lt;b&gt;<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; von „ungefähr“ zu „genau“ ändern?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, den ungefähren Gerätestandort abzurufen?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf den ungefähren Standort deines Geräts &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; zugreifen?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Genau"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Ungefähr"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf deinen Kalender zuzugreifen?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf deinen Kalender zugreifen?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, SMS zu senden und aufzurufen?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; SMS abrufen und ansehen?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Fotos, Medien und Dateien auf deinem Gerät zuzugreifen?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf Fotos, Medien und Dateien zugreifen?"</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; erlauben, auf &lt;b&gt;Foto-, Video-, Musik- und Audiodateien&lt;/b&gt; auf diesem Gerät zuzugreifen?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf &lt;b&gt;Foto-, Video-, Musik-, Audio- und andere Dateien&lt;/b&gt; auf diesem Gerät zuzugreifen?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Musik- und Audiodateien auf diesem Gerät zuzugreifen?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf Musik- und Audiodateien zugreifen?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf Fotos und Videos auf diesem Gerät zuzugreifen?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf Fotos und Videos zugreifen?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf weitere Fotos und Videos auf diesem Gerät zugreifen?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf weitere Fotos und Videos zugreifen?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Audioaufnahmen zu machen?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; Audioaufnahmen machen?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Diese App kann nur Audioaufnahmen machen, solange du sie verwendest"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Audioaufnahmen zu machen?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; Audioaufnahmen machen?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Diese App möchte ggf. jederzeit Audioaufnahmen machen können, auch wenn du sie nicht verwendest. "<annotation id="link">"Du kannst das in den Einstellungen zulassen."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Mikrofonzugriff für &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ändern?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Mikrofonzugriff für &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ändern?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Diese App möchte jederzeit Audioaufnahmen machen können, auch wenn du sie nicht verwendest. "<annotation id="link">"Du kannst das in den Einstellungen zulassen."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, auf deine körperliche Aktivität zuzugreifen?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf die Daten zu deinen körperlichen Aktivitäten zugreifen?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Bilder und Videos aufzunehmen?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mit deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; Foto- und Videoaufnahmen machen?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Diese App kann nur Bilder und Videos aufnehmen, solange du sie verwendest"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, Bilder und Videos aufzunehmen?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mit deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; Foto- und Videoaufnahmen machen?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Diese App möchte ggf. 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="permgroupupgraderequest_camera" msgid="640758449200241582">"Kamerazugriff für &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ändern?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Kamerazugriff für &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ändern?"</string>
<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_device_aware_calllog" msgid="8220927190376843309">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf deine Anrufliste zugreifen?"</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_device_aware_phone" msgid="590399263670349955">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; Anrufe tätigen und verwalten?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf Sensordaten zu deinen Vitalzeichen zugreifen?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Darf &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; auf die Sensordaten zu deinen Vitalzeichen zugreifen?"</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>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; Zugriff auf Körpersensordaten bei Verwendung weiter erlauben?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Darf die App &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; weiterhin auf deine Körpersensordaten zugreifen, während die App genutzt wird?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erlauben, dir Benachrichtigungen zu senden?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Darf dir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; auf deinem Gerät &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; Benachrichtigungen senden?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Erteilte Berechtigungen"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> hat Standortzugriff"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Deine Organisation erlaubt <xliff:g id="APP_NAME">%1$s</xliff:g> den Zugriff auf deinen Standort"</string>
@@ -512,6 +551,9 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Keine"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Letzte\n24 Stunden"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Letzte\n7 Tage"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for privdash_usage_percent (6893824766124414127) -->
+ <skip />
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist durch Android geschützt. Weil deine Daten direkt auf diesem Gerät verarbeitet werden, wird weder in der Statusleiste noch auf deinem Privatsphäredashboard die Nutzung der Berechtigungen durch diese App angezeigt."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist durch Android geschützt. Weil deine Daten direkt auf diesem Gerät verarbeitet werden, wird auf deinem Privatsphäredashboard die Nutzung der Berechtigungen durch diese App nicht angezeigt."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Gerätekamera ist blockiert"</string>
@@ -520,6 +562,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Zugriff auf die Kamera ist deaktiviert"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofonzugriff deaktiviert"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Standortzugriff deaktiviert"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Für Infotainment-Apps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Für erforderliche Apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Diese App ist erforderlich"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Diese App wird vom Fahrzeughersteller vorgeschrieben"</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>
@@ -580,8 +629,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>
@@ -610,7 +659,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>
@@ -619,4 +668,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Änderungen bei der Datenweitergabepraxis"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Bei einigen Apps hat sich die Art der Weitergabe deiner Standortdaten geändert"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Einstellungen"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Zugriff: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Zugriff: gestern, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Zugriff: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Dein Einmalpasswort lautet 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Eingeschränkte Einstellung"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Aus Sicherheitsgründen ist diese Einstellung derzeit nicht verfügbar."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App wurde Zugriff auf „<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>“ verweigert"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Die App hat Zugriff auf eine vertrauliche Berechtigung angefordert. Wenn du diesen zulässt, sind deine privaten Daten und Finanzdaten eventuell gefährdet.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Die App funktioniert ohne diese eingeschränkte Berechtigung aber möglicherweise nicht richtig. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Informationen dazu, wie du den Zugriff erlaubst&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App wurde der Zugriff verweigert, die standardmäßige <xliff:g id="ROLE_NAME">%1$s</xliff:g> zu sein"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Die App hat Zugriff auf vertrauliche Berechtigungen angefordert. Wenn du diesen zulässt, sind deine privaten Daten und Finanzdaten eventuell gefährdet.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Die App funktioniert ohne diese eingeschränkten Berechtigungen aber möglicherweise nicht richtig. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Informationen dazu, wie du den Zugriff erlaubst&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"App wurde Zugriff verweigert"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Wenn du den Zugriff auf diese Berechtigung zulässt, sind deine privaten Daten und Finanzdaten eventuell gefährdet.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Die App funktioniert ohne diese eingeschränkte Berechtigung aber möglicherweise nicht richtig. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Informationen dazu, wie du den Zugriff erlaubst&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Weitere Informationen"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Ok"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Berechtigungsanfrage unterdrückt"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Diese App fordert zusätzliche Berechtigungen an – in einer Streamingsitzung können jedoch keine Berechtigungen gewährt werden. Erteile zuerst die Berechtigung auf deinem Smartphone."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Für Notrufe oder Nachrichten an eine Notrufnummer"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Standort wurde an den Rettungsdienst gesendet"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Diese App hat den Standort deines Geräts abgerufen, während ein Notruf getätigt oder eine Nachricht an eine Notrufnummer gesendet wurde. Das kann auch dann passieren, wenn die App keine Berechtigung zur Standortermittlung hat oder der Gerätestandort deaktiviert ist. "<a href="https://support.google.com/android/answer/9319337">"Weitere Informationen"</a></string>
</resources>
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-v34/strings.xml b/PermissionController/res/values-el-v34/strings.xml
index 0c8541787..b7486d405 100644
--- a/PermissionController/res/values-el-v34/strings.xml
+++ b/PermissionController/res/values-el-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-el-watch/strings.xml b/PermissionController/res/values-el-watch/strings.xml
index 05063253d..6f461e93d 100644
--- a/PermissionController/res/values-el-watch/strings.xml
+++ b/PermissionController/res/values-el-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Αδυναμία αλλαγής"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ναι"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Ακύρωση"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Να επιτρέπεται πάντα"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Με τη χρήση της εφαρμογής"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Να επιτρέπεται πάντα"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Με τη χρήση της εφαρμογής"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Να επιτρέπεται πάντα"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Με τη χρήση της εφαρμογής"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Να επιτρέπεται πάντα"</string>
</resources>
diff --git a/PermissionController/res/values-el/strings.xml b/PermissionController/res/values-el/strings.xml
index 54056ef01..3693e3677 100644
--- a/PermissionController/res/values-el/strings.xml
+++ b/PermissionController/res/values-el/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"άδειες"</string>
<string name="cancel" msgid="8943320028373963831">"Ακύρωση"</string>
<string name="back" msgid="6249950659061523680">"Πίσω"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Κλείσιμο"</string>
<string name="available" msgid="6007778121920339498">"Διαθέσιμο"</string>
<string name="blocked" msgid="9195547604866033708">"Αποκλεισμένο"</string>
<string name="on" msgid="280241003226755921">"Ενεργό"</string>
@@ -34,6 +35,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 +55,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Οι ενέργειες εγκατάστασης/απεγκατάστασης δεν υποστηρίζονται στο Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Επιλέξτε σε τι θα έχει πρόσβαση η εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Η εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ενημερώθηκε. Επιλέξτε σε τι θα έχει πρόσβαση αυτή η εφαρμογή."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Ακύρωση"</string>
@@ -191,20 +192,24 @@
<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_limited_access" msgid="8824410215149764113">"Να επιτρέπεται περιορισμένη πρόσβαση"</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_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_with_device_name" msgid="7193042925656173271">"Πρόσβαση στην άδεια <xliff:g id="PERM">%1$s</xliff:g> για αυτή την εφαρμογή στη συσκευή <xliff:g id="DEVICE_NAME">%2$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" msgid="2595428768404901064">"Κατάργηση αδειών και αποδέσμευση χώρου"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Παύση δραστηριότητας αδρανούς εφαρμογής"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Διαχείρ. εφαρμ. αν δεν χρησιμοποιείται"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Κατάργηση αδειών, διαγραφή προσωρινών αρχείων και διακοπή ειδοποιήσεων"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Κατάργηση αδειών, διαγραφή προσωρινών αρχείων, διακοπή ειδοποιήσεων και αρχειοθέτηση της εφαρμογής"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Για την προστασία των δεδομένων σας, οι άδειες για αυτή την εφαρμογή θα καταργηθούν εάν η εφαρμογή δεν χρησιμοποιηθεί για μερικούς μήνες."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Για την προστασία των δεδομένων σας, εάν δεν έχει χρησιμοποιηθεί η εφαρμογή για μερικούς μήνες, οι παρακάτω άδειες θα καταργηθούν: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Για την προστασία των δεδομένων σας, έχουν καταργηθεί οι άδειες εφαρμογών που δεν έχετε χρησιμοποιήσει κατά τους τελευταίους μήνες."</string>
@@ -219,8 +224,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Επιτρέπεται η διαχείριση όλων των αρχείων"</string>
<string name="ask_header" msgid="2633816846459944376">"Ερώτηση κάθε φορά"</string>
<string name="denied_header" msgid="903209608358177654">"Δεν επιτρέπεται"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> στη συσκευή <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Δείτε περισσότερες εφαρμογές με πρόσβαση σε όλα τα αρχεία"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ημέρα}other{# ημέρες}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ώρα}other{# ώρες}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Προεπιλ. εφαρμογή πορτοφολιού"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Εφαρμογή πορτοφολιού"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Οι εφαρμογές πορτοφολιού μπορούν να αποθηκεύσουν πιστωτικές κάρτες και κάρτες επιβράβευσης αφοσιωμένων πελατών, κλειδιά αυτοκινήτου και άλλα στοιχεία για να σας βοηθήσουν με διάφορες μορφές συναλλαγών."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Ορισμός της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g> ως προεπιλεγμένης εφαρμογής πορτοφολιού;"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Δεν απαιτούνται άδειες"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Τρέχουσα προεπιλογή"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Να μην ερωτηθώ ξανά"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Προεπιλογή"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Περισσότερες προεπιλογές"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Άνοιγμα συνδέσμων"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Προεπιλογή για εργασία"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Προεπιλογή για ιδιωτικό χώρο"</string>
<string name="default_app_none" msgid="9084592086808194457">"Καμία"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Προεπιλογή συστήματος)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Δεν υπάρχουν εφαρμογές"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε φωτογραφίες και μέσα στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στις επαφές σας στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στην τοποθεσία της συσκευής &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_location" msgid="1264484517831380016">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στην τοποθεσία της συσκευής &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Να γίνει αλλαγή της πρόσβασης σε τοποθεσία για την εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;;"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Αυτή η εφαρμογή θέλει να έχει συνεχώς πρόσβαση στην τοποθεσία σας, ακόμη και όταν δεν χρησιμοποιείτε την εφαρμογή. "<annotation id="link">"Εγκρίνετε το αίτημα στις ρυθμίσεις."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Να επιτρέπεται η εύρεση, η σύνδεση κι ο προσδιορισμός σχετικής τοποθεσίας των κοντινών συσκευών από &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;;"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Να επιτρέπεται στο &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εύρεση, η σύνδεση κι ο προσδιορισμός της σχετικής τοποθεσίας των κοντινών συσκευών;"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εύρεση και η σύνδεση σε κοντινές συσκευές και ο προσδιορισμός της σχετικής θέσης τους στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"Να γίνει αλλαγή της πρόσβασης της εφαρμογής <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> στην τοποθεσία της συσκευής &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στην τοποθεσία κατά προσέγγιση της συσκευής &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στο ημερολόγιό σας στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_sms" msgid="6639977653040502291">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η αποστολή και η προβολή μηνυμάτων SMS στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε φωτογραφίες, μέσα και αρχεία στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε μουσική και σε ήχο στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε φωτογραφίες και σε βίντεο στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε περισσότερες φωτογραφίες και βίντεο στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εγγραφή ήχου στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η εγγραφή ήχου στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Να γίνει αλλαγή της πρόσβασης μικροφώνου για την εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στη σωματική δραστηριότητά σας στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η λήψη φωτογραφιών και η εγγραφή βίντεο στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η λήψη φωτογραφιών και η εγγραφή βίντεο στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Να γίνει αλλαγή της πρόσβασης κάμερας για την εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στα αρχεία καταγραφής κλήσεων τηλεφώνου σας στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πραγματοποίηση και η διαχείριση τηλεφωνικών κλήσεων στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στα δεδομένα αισθητήρων των ζωτικών λειτουργιών σας στο &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση στα δεδομένα αισθητήρων των ζωτικών λειτουργιών σας στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;;"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Για να επιτρέψετε σε αυτή την εφαρμογή να έχει πρόσβαση σε δεδομένα αισθητήρων σώματος οποιαδήποτε στιγμή, ακόμα και όταν δεν χρησιμοποιείτε την εφαρμογή, "<annotation id="link">"μεταβείτε στις ρυθμίσεις."</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_device_aware_sensors" msgid="5542771499929819675">"Να συνεχίσει να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η πρόσβαση σε δεδομένα αισθητήρων σώματος στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Να επιτρέπεται στην εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; η αποστολή ειδοποιήσεων στη συσκευή &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Καμία"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Τελευταίες\n24 ώρες"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Τελευταίες\n7 ημέρες"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> τοις εκατό"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> προστατεύεται από το Android. Επειδή η επεξεργασία των δεδομένων σας πραγματοποιείται σε αυτήν τη συσκευή, η χρήση αδειών αυτής της εφαρμογής δεν εμφανίζεται στη γραμμή κατάστασης ή στον πίνακα ελέγχου απορρήτου σας."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Η πρόσβαση στην κάμερα είναι απενεργοποιημένη"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Η πρόσβαση μικροφώνου είναι ανενεργή"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Η πρόσβαση τοποθεσίας είναι ανενεργή"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Για εφαρμογές ενημέρωσης και ψυχαγωγίας"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Για απαιτούμενες εφαρμογές"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Απαιτείται αυτή η εφαρμογή"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Αυτή η εφαρμογή απαιτείται από τον κατασκευαστή του αυτοκινήτου σας"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Πρόσβαση στις <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Πρόσβαση χθες, στις <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Πρόσβαση στις <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Ο κωδικός πρόσβασης μίας χρήσης είναι 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Περιορισμένη ρύθμιση"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Για την ασφάλειά σας, αυτή η ρύθμιση δεν είναι διαθέσιμη αυτή τη στιγμή."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Απορρίφθηκε η πρόσβαση της εφαρμογής στην άδεια <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Η εφαρμογή ζήτησε πρόσβαση σε μια άδεια πρόσβασης σε ευαίσθητες πληροφορίες, γεγονός που μπορεί να θέσει σε κίνδυνο τα προσωπικά και οικονομικά στοιχεία σας.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Είναι πιθανό η εφαρμογή να μην λειτουργεί σωστά χωρίς αυτή την περιορισμένη άδεια. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Μάθετε πώς μπορείτε να επιτρέψετε την πρόσβαση&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Δεν επιτράπηκε στην εφαρμογή να οριστεί ως η προεπιλεγμένη <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Η εφαρμογή ζήτησε πρόσβαση σε άδειες πρόσβασης σε ευαίσθητες πληροφορίες, γεγονός που μπορεί να θέσει σε κίνδυνο τα προσωπικά και οικονομικά στοιχεία σας.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Είναι πιθανό η εφαρμογή να μην λειτουργεί σωστά χωρίς αυτές τις περιορισμένες άδειες. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Μάθετε πώς μπορείτε να επιτρέψετε την πρόσβαση&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Απορρίφθηκε η πρόσβαση της εφαρμογής"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Η πρόσβαση σε αυτή την άδεια μπορεί να θέσει σε κίνδυνο τα προσωπικά και οικονομικά στοιχεία σας.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Είναι πιθανό η εφαρμογή να μην λειτουργεί σωστά χωρίς αυτή την περιορισμένη άδεια. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Μάθετε πώς μπορείτε να επιτρέψετε την πρόσβαση&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Μάθετε περισσότερα"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ΟΚ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Το αίτημα άδειας ανεστάλη"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Αυτή η εφαρμογή ζητά πρόσθετες άδειες, αλλά οι άδειες δεν μπορούν να εκχωρηθούν σε μια περίοδο σύνδεσης ροής. Εκχωρήστε πρώτα την άδεια στο τηλέφωνό σας."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Για κλήσεις ή μηνύματα έκτακτης ανάγκης"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Η τοποθεσία στάλθηκε στις υπηρεσίες έκτακτης ανάγκης"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Αυτή η εφαρμογή απέκτησε πρόσβαση στην τοποθεσία της συσκευής σας κατά τη διάρκεια μιας κλήσης ή κατά την αποστολή ενός μηνύματος σε έναν αριθμό έκτακτης ανάγκης. Αυτό μπορεί να συμβεί ακόμα και εάν η εφαρμογή δεν έχει άδεια τοποθεσίας ή εάν η τοποθεσία της συσκευής είναι απενεργοποιημένη. "<a href="https://support.google.com/android/answer/9319337">"Μάθετε περισσότερα"</a></string>
</resources>
diff --git a/PermissionController/res/values-en-rAU-v34/strings.xml b/PermissionController/res/values-en-rAU-v34/strings.xml
index 4800f8a91..f16d34825 100644
--- a/PermissionController/res/values-en-rAU-v34/strings.xml
+++ b/PermissionController/res/values-en-rAU-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Manage app access to health data"</string>
<string name="location_settings" msgid="8863940440881290182">"Location access"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"For apps and services"</string>
</resources>
diff --git a/PermissionController/res/values-en-rAU-watch/strings.xml b/PermissionController/res/values-en-rAU-watch/strings.xml
index 70643f521..e3f48cb2c 100644
--- a/PermissionController/res/values-en-rAU-watch/strings.xml
+++ b/PermissionController/res/values-en-rAU-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Can\'t be changed"</string>
<string name="generic_yes" msgid="2489207724988649846">"Yes"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancel"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"All the time"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"While using app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"All the time"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"While using app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"All the time"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"While using app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"All the time"</string>
</resources>
diff --git a/PermissionController/res/values-en-rAU/strings.xml b/PermissionController/res/values-en-rAU/strings.xml
index 77c6837ad..e224415da 100644
--- a/PermissionController/res/values-en-rAU/strings.xml
+++ b/PermissionController/res/values-en-rAU/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string>
<string name="cancel" msgid="8943320028373963831">"Cancel"</string>
<string name="back" msgid="6249950659061523680">"Back"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Close"</string>
<string name="available" msgid="6007778121920339498">"Available"</string>
<string name="blocked" msgid="9195547604866033708">"Blocked"</string>
<string name="on" msgid="280241003226755921">"On"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"All permissions"</string>
<string name="other_permissions" msgid="2901186127193849594">"Other app capabilities"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Permission request"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Install/Uninstall actions not supported on Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what access to allow this app."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancel"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Always allow all"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Allow limited access"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Approximate location"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Use precise location"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"When precise location is off, apps can access your approximate location"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> permission"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> access for this app"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> access for this app on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"See all <xliff:g id="APP">%1$s</xliff:g> permissions"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"See all apps with this permission"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Show Assistant microphone usage"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Remove permissions if app isn’t used"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remove permissions and free up space"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pause app activity if unused"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Manage app if unused"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissions, delete temporary files and stop notifications"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remove permissions, delete temporary files, stop notifications and archive the app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"To protect your data, permissions for this app will be removed if the app is unused for a few months."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"To protect your data, if the app is unused for a few months, the following permissions will be removed: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"To protect your data, permissions have been removed from apps that you haven’t used in a few months."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Allowed to manage all files"</string>
<string name="ask_header" msgid="2633816846459944376">"Ask every time"</string>
<string name="denied_header" msgid="903209608358177654">"Not allowed"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string>
@@ -349,7 +355,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notes app"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps that allow you to take notes on your device"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notes"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Default wallet app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Wallet apps can store your credit and loyalty cards, car keys and other things to help with various forms of transactions."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default wallet app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No permissions needed"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Current default"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Don\'t ask again"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Set as default"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"More defaults"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Opening links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Default for work"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Default for private space"</string>
<string name="default_app_none" msgid="9084592086808194457">"None"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Show assistant trigger detection"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Show icon in status bar when microphone is used to activate voice assistant"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\'s&lt;/b&gt; location?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"The app will only have access to the location while you\'re using the app"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\'s&lt;/b&gt; location?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"This app may want to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"This app wants to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in Settings."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of Nearby devices on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices? "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>’s location access from approximate to precise?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\'s location access on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; from approximate to precise?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s approximate location?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;\'s approximate location?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precise"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximate"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music and audio&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"This app may want to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"This app wants to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"The app will only be able to take pictures and record video while you’re using the app"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"This app may want to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"This app wants to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, "<annotation id="link">"go to Settings."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"To let this app access body sensor data all the time, even when you’re not using the app, "<annotation id="link">"go to settings."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data while the app is in use?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; while app is in use?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Controlled permissions"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> has location access"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Your organisation allows <xliff:g id="APP_NAME">%1$s</xliff:g> to access your location"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"None"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Past\n24 hours"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Past\n7 days"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> per cent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on the status bar or your privacy dashboard."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on your privacy dashboard."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Device camera is blocked"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"For apps and services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Microphone data may still be shared when you call an emergency number."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Change"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Camera access is off"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Microphone access is off"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Location access is off"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"For infotainment apps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"For required apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"This app is required"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"This app is required by your car\'s manufacturer"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Security and privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Scan device"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dismiss"</string>
@@ -531,7 +578,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Data sharing updates"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Some apps changed the way that they may share your location data"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Settings"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Accessed <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Your one-time password is 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"The app requested access to sensitive permissions which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without these restricted permissions. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"App was denied access"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Access to this permission can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Learn more"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can\'t be granted in a streaming session. Grant the permission on your phone first."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string>
</resources>
diff --git a/PermissionController/res/values-en-rCA-v34/strings.xml b/PermissionController/res/values-en-rCA-v34/strings.xml
index 4800f8a91..f16d34825 100644
--- a/PermissionController/res/values-en-rCA-v34/strings.xml
+++ b/PermissionController/res/values-en-rCA-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Manage app access to health data"</string>
<string name="location_settings" msgid="8863940440881290182">"Location access"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"For apps and services"</string>
</resources>
diff --git a/PermissionController/res/values-en-rCA-watch/strings.xml b/PermissionController/res/values-en-rCA-watch/strings.xml
index adecb65b9..aa97f28cc 100644
--- a/PermissionController/res/values-en-rCA-watch/strings.xml
+++ b/PermissionController/res/values-en-rCA-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Can\'t be changed"</string>
<string name="generic_yes" msgid="2489207724988649846">"Yes"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancel"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"All the time"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"While using app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"All the time"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"While using app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"All the time"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"While using app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"All the time"</string>
</resources>
diff --git a/PermissionController/res/values-en-rCA/strings.xml b/PermissionController/res/values-en-rCA/strings.xml
index fc83ad25a..c21dc2f8c 100644
--- a/PermissionController/res/values-en-rCA/strings.xml
+++ b/PermissionController/res/values-en-rCA/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string>
<string name="cancel" msgid="8943320028373963831">"Cancel"</string>
<string name="back" msgid="6249950659061523680">"Back"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Close"</string>
<string name="available" msgid="6007778121920339498">"Available"</string>
<string name="blocked" msgid="9195547604866033708">"Blocked"</string>
<string name="on" msgid="280241003226755921">"On"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"All permissions"</string>
<string name="other_permissions" msgid="2901186127193849594">"Other app capabilities"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Permission request"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Install/Uninstall actions not supported on Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what to allow this app to access."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancel"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Always allow all"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Allow limited access"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Approximate location"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Use precise location"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"When precise location is off, apps can access your approximate location"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> permission"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> access for this app"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> access for this app on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"See all <xliff:g id="APP">%1$s</xliff:g> permissions"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"See all apps with this permission"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Show assistant microphone usage"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Remove permissions if app isn’t used"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remove permissions and free up space"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pause app activity if unused"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Manage app if unused"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissions, delete temporary files, and stop notifications"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remove permissions, delete temporary files, stop notifications, and archive the app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"To protect your data, permissions for this app will be removed if the app is unused for a few months."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"To protect your data, if the app is unused for a few months, the following permissions will be removed: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"To protect your data, permissions have been removed from apps that you haven’t used in a few months."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Allowed to manage all files"</string>
<string name="ask_header" msgid="2633816846459944376">"Ask every time"</string>
<string name="denied_header" msgid="903209608358177654">"Not allowed"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notes app"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps that allow you to take notes on your device"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notes"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Default wallet app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Wallet apps can store your credit and loyalty cards, car keys and other things to help with various forms of transactions."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default wallet app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No permissions needed"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Current default"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Don’t ask again"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Set as default"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"More defaults"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Opening links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Default for work"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Default for private space"</string>
<string name="default_app_none" msgid="9084592086808194457">"None"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Show assistant trigger detection"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Show icon in status bar when microphone is used to activate voice assistant"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; location?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"The app will only have access to the location while you\'re using the app"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; location?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"This app may want to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"This app wants to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices? "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>’s location access from approximate to precise?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>’s location access on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; from approximate to precise?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s approximate location?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;’s approximate location?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precise"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximate"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media, and files on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media, and files on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, and audio&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio, and other files&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"This app may want to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"This app wants to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"The app will only be able to take pictures and record video while you’re using the app"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"This app may want to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"This app wants to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, "<annotation id="link">"go to settings."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"To let this app access body sensor data all the time, even when you’re not using the app, "<annotation id="link">"go to settings."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data while app is in use?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; while app is in use?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Controlled permissions"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> has location access"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Your organization allows <xliff:g id="APP_NAME">%1$s</xliff:g> to access your location"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"None"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Past\n24 hours"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Past\n7 days"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> percent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on the status bar or your privacy dashboard."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on your privacy dashboard."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Device camera is blocked"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"For apps and services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Microphone data may still be shared when you call an emergency number."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Change"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Camera access is off"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Microphone access is off"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Location access is off"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"For infotainment apps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"For required apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"This app is required"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"This app is required by your car’s manufacturer"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Security and privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Scan device"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dismiss"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Data sharing updates"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Some apps changed the way they may share your location data"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Settings"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Accessed <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Your one time password is 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"The app requested access to sensitive permissions which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without these restricted permissions. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"App was denied access"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Access to this permission can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Learn more"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can’t be granted in a streaming session. Grant the permission on your phone first."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string>
</resources>
diff --git a/PermissionController/res/values-en-rGB-v34/strings.xml b/PermissionController/res/values-en-rGB-v34/strings.xml
index 4800f8a91..f16d34825 100644
--- a/PermissionController/res/values-en-rGB-v34/strings.xml
+++ b/PermissionController/res/values-en-rGB-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Manage app access to health data"</string>
<string name="location_settings" msgid="8863940440881290182">"Location access"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"For apps and services"</string>
</resources>
diff --git a/PermissionController/res/values-en-rGB-watch/strings.xml b/PermissionController/res/values-en-rGB-watch/strings.xml
index 70643f521..e3f48cb2c 100644
--- a/PermissionController/res/values-en-rGB-watch/strings.xml
+++ b/PermissionController/res/values-en-rGB-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Can\'t be changed"</string>
<string name="generic_yes" msgid="2489207724988649846">"Yes"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancel"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"All the time"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"While using app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"All the time"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"While using app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"All the time"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"While using app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"All the time"</string>
</resources>
diff --git a/PermissionController/res/values-en-rGB/strings.xml b/PermissionController/res/values-en-rGB/strings.xml
index 9feaad707..306d72421 100644
--- a/PermissionController/res/values-en-rGB/strings.xml
+++ b/PermissionController/res/values-en-rGB/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string>
<string name="cancel" msgid="8943320028373963831">"Cancel"</string>
<string name="back" msgid="6249950659061523680">"Back"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Close"</string>
<string name="available" msgid="6007778121920339498">"Available"</string>
<string name="blocked" msgid="9195547604866033708">"Blocked"</string>
<string name="on" msgid="280241003226755921">"On"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"All permissions"</string>
<string name="other_permissions" msgid="2901186127193849594">"Other app capabilities"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Permission request"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Install/Uninstall actions not supported on Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what access to allow this app."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancel"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Always allow all"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Allow limited access"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Approximate location"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Use precise location"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"When precise location is off, apps can access your approximate location"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> permission"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> access for this app"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> access for this app on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"See all <xliff:g id="APP">%1$s</xliff:g> permissions"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"See all apps with this permission"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Show Assistant microphone usage"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Remove permissions if app isn’t used"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remove permissions and free up space"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pause app activity if unused"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Manage app if unused"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissions, delete temporary files and stop notifications"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remove permissions, delete temporary files, stop notifications and archive the app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"To protect your data, permissions for this app will be removed if the app is unused for a few months."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"To protect your data, if the app is unused for a few months, the following permissions will be removed: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"To protect your data, permissions have been removed from apps that you haven’t used in a few months."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Allowed to manage all files"</string>
<string name="ask_header" msgid="2633816846459944376">"Ask every time"</string>
<string name="denied_header" msgid="903209608358177654">"Not allowed"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string>
@@ -349,7 +355,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notes app"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps that allow you to take notes on your device"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notes"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Default wallet app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Wallet apps can store your credit and loyalty cards, car keys and other things to help with various forms of transactions."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default wallet app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No permissions needed"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Current default"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Don\'t ask again"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Set as default"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"More defaults"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Opening links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Default for work"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Default for private space"</string>
<string name="default_app_none" msgid="9084592086808194457">"None"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Show assistant trigger detection"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Show icon in status bar when microphone is used to activate voice assistant"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\'s&lt;/b&gt; location?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"The app will only have access to the location while you’re using the app"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\'s&lt;/b&gt; location?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"This app may want to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"This app wants to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in Settings."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of Nearby devices on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices? "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>’s location access from approximate to precise?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\'s location access on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; from approximate to precise?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s approximate location?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;\'s approximate location?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precise"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximate"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music and audio&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"This app may want to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"This app wants to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"The app will only be able to take pictures and record video while you’re using the app"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"This app may want to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"This app wants to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, "<annotation id="link">"go to Settings."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"To let this app access body sensor data all the time, even when you’re not using the app, "<annotation id="link">"go to settings."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data while the app is in use?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; while app is in use?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Controlled permissions"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> has location access"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Your organisation allows <xliff:g id="APP_NAME">%1$s</xliff:g> to access your location"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"None"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Past\n24 hours"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Past\n7 days"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> per cent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on the status bar or your privacy dashboard."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on your privacy dashboard."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Device camera is blocked"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"For apps and services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Microphone data may still be shared when you call an emergency number."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Change"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Camera access is off"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Microphone access is off"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Location access is off"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"For infotainment apps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"For required apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"This app is required"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"This app is required by your car\'s manufacturer"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Security and privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Scan device"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dismiss"</string>
@@ -531,7 +578,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Data sharing updates"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Some apps changed the way that they may share your location data"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Settings"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Accessed <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Your one-time password is 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"The app requested access to sensitive permissions which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without these restricted permissions. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"App was denied access"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Access to this permission can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Learn more"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can\'t be granted in a streaming session. Grant the permission on your phone first."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string>
</resources>
diff --git a/PermissionController/res/values-en-rIN-v34/strings.xml b/PermissionController/res/values-en-rIN-v34/strings.xml
index 4800f8a91..f16d34825 100644
--- a/PermissionController/res/values-en-rIN-v34/strings.xml
+++ b/PermissionController/res/values-en-rIN-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Manage app access to health data"</string>
<string name="location_settings" msgid="8863940440881290182">"Location access"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"For apps and services"</string>
</resources>
diff --git a/PermissionController/res/values-en-rIN-watch/strings.xml b/PermissionController/res/values-en-rIN-watch/strings.xml
index 70643f521..e3f48cb2c 100644
--- a/PermissionController/res/values-en-rIN-watch/strings.xml
+++ b/PermissionController/res/values-en-rIN-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Can\'t be changed"</string>
<string name="generic_yes" msgid="2489207724988649846">"Yes"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancel"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"All the time"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"While using app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"All the time"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"While using app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"All the time"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"While using app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"All the time"</string>
</resources>
diff --git a/PermissionController/res/values-en-rIN/strings.xml b/PermissionController/res/values-en-rIN/strings.xml
index 9feaad707..306d72421 100644
--- a/PermissionController/res/values-en-rIN/strings.xml
+++ b/PermissionController/res/values-en-rIN/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permissions"</string>
<string name="cancel" msgid="8943320028373963831">"Cancel"</string>
<string name="back" msgid="6249950659061523680">"Back"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Close"</string>
<string name="available" msgid="6007778121920339498">"Available"</string>
<string name="blocked" msgid="9195547604866033708">"Blocked"</string>
<string name="on" msgid="280241003226755921">"On"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"All permissions"</string>
<string name="other_permissions" msgid="2901186127193849594">"Other app capabilities"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Permission request"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Install/Uninstall actions not supported on Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Choose what to allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; has been updated. Choose what access to allow this app."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancel"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Always allow all"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Ask every time"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Don\'t allow"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Allow limited access"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Precise location"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Approximate location"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Use precise location"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"When precise location is off, apps can access your approximate location"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> permission"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g> access for this app"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> access for this app on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"See all <xliff:g id="APP">%1$s</xliff:g> permissions"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"See all apps with this permission"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Show Assistant microphone usage"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Remove permissions if app isn’t used"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remove permissions and free up space"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pause app activity if unused"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Manage app if unused"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissions, delete temporary files and stop notifications"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remove permissions, delete temporary files, stop notifications and archive the app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"To protect your data, permissions for this app will be removed if the app is unused for a few months."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"To protect your data, if the app is unused for a few months, the following permissions will be removed: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"To protect your data, permissions have been removed from apps that you haven’t used in a few months."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Allowed to manage all files"</string>
<string name="ask_header" msgid="2633816846459944376">"Ask every time"</string>
<string name="denied_header" msgid="903209608358177654">"Not allowed"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> on <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"See more apps that can access all files"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 day}other{# days}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hour}other{# hours}}"</string>
@@ -349,7 +355,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notes app"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps that allow you to take notes on your device"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notes"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Default wallet app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Wallet apps can store your credit and loyalty cards, car keys and other things to help with various forms of transactions."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Set <xliff:g id="APP_NAME">%1$s</xliff:g> as your default wallet app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No permissions needed"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Current default"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Don\'t ask again"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Set as default"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"More defaults"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Opening links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Default for work"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Default for private space"</string>
<string name="default_app_none" msgid="9084592086808194457">"None"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(System default)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"No apps"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Show assistant trigger detection"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Show icon in status bar when microphone is used to activate voice assistant"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and media on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your contacts on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\'s&lt;/b&gt; location?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"The app will only have access to the location while you’re using the app"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s location?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\'s&lt;/b&gt; location?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"This app may want to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Change location access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"This app wants to access your location all the time, even when you’re not using the app. "<annotation id="link">"Allow in Settings."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of Nearby devices on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to find, connect to and determine the relative position of nearby devices? "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>’s location access from approximate to precise?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Change <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\'s location access on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; from approximate to precise?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access this device’s approximate location?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;\'s approximate location?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precise"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximate"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your calendar on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send and view SMS messages on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on your device?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos, media and files on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music and audio&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio and other files&lt;/b&gt; on this device?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access music and audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on this device?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access more photos and videos on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"The app will only be able to record audio while you’re using the app"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to record audio on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"This app may want to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Change microphone access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"This app wants to record audio all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your physical activity on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"The app will only be able to take pictures and record video while you’re using the app"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to take pictures and record video on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"This app may want to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Change camera access for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"This app wants to take pictures and record video all the time, even when you’re not using the app. "<annotation id="link">"Allow in settings."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access your phone call logs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to make and manage phone calls on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, "<annotation id="link">"go to Settings."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access the sensor data about your vital signs on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"To let this app access body sensor data all the time, even when you’re not using the app, "<annotation id="link">"go to settings."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data while the app is in use?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Keep allowing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to access body sensor data on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; while app is in use?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Allow &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; to send you notifications on &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Controlled permissions"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> has location access"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Your organisation allows <xliff:g id="APP_NAME">%1$s</xliff:g> to access your location"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"None"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Past\n24 hours"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Past\n7 days"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> per cent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on the status bar or your privacy dashboard."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on your privacy dashboard."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Device camera is blocked"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"For apps and services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Microphone data may still be shared when you call an emergency number."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Change"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Camera access is off"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Microphone access is off"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Location access is off"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"For infotainment apps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"For required apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"This app is required"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"This app is required by your car\'s manufacturer"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Security and privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Scan device"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dismiss"</string>
@@ -531,7 +578,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Data sharing updates"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Some apps changed the way that they may share your location data"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Settings"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Accessed <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accessed yesterday <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accessed <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Your one-time password is 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Restricted setting"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"For your security, this setting is currently unavailable."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App was denied access to <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App was denied access to be default <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"The app requested access to sensitive permissions which can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without these restricted permissions. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"App was denied access"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Access to this permission can put your personal and financial info at risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible that the app won\'t work properly without this restricted permission. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Learn how to allow access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Learn more"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permission request suppressed"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"This app is requesting additional permissions, but permissions can\'t be granted in a streaming session. Grant the permission on your phone first."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For emergency call or text"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Location sent to emergency services"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. "<a href="https://support.google.com/android/answer/9319337">"Learn more"</a></string>
</resources>
diff --git a/PermissionController/res/values-en-rXC-v34/strings.xml b/PermissionController/res/values-en-rXC-v34/strings.xml
index 525a410b1..91d6fc736 100644
--- a/PermissionController/res/values-en-rXC-v34/strings.xml
+++ b/PermissionController/res/values-en-rXC-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‎‎Manage app access to health data‎‏‎‎‏‎"</string>
<string name="location_settings" msgid="8863940440881290182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎Location access‎‏‎‎‏‎"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎For apps and services. If this setting is off, microphone data may still be shared when you call an emergency number‎‏‎‎‏‎"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎For apps and services‎‏‎‎‏‎"</string>
</resources>
diff --git a/PermissionController/res/values-en-rXC-watch/strings.xml b/PermissionController/res/values-en-rXC-watch/strings.xml
index 75a8de56f..c8631b66a 100644
--- a/PermissionController/res/values-en-rXC-watch/strings.xml
+++ b/PermissionController/res/values-en-rXC-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎Can\'t be changed‎‏‎‎‏‎"</string>
<string name="generic_yes" msgid="2489207724988649846">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎Yes‎‏‎‎‏‎"</string>
<string name="generic_cancel" msgid="2631708607129269698">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎Cancel‎‏‎‎‏‎"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎All the time‎‏‎‎‏‎"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎While using app‎‏‎‎‏‎"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎All the time‎‏‎‎‏‎"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎While using app‎‏‎‎‏‎"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎All the time‎‏‎‎‏‎"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎While using app‎‏‎‎‏‎"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‎‏‎‏‎All the time‎‏‎‎‏‎"</string>
</resources>
diff --git a/PermissionController/res/values-en-rXC/strings.xml b/PermissionController/res/values-en-rXC/strings.xml
index c712a6f2d..0775a781f 100644
--- a/PermissionController/res/values-en-rXC/strings.xml
+++ b/PermissionController/res/values-en-rXC/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎permissions‎‏‎‎‏‎"</string>
<string name="cancel" msgid="8943320028373963831">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎Cancel‎‏‎‎‏‎"</string>
<string name="back" msgid="6249950659061523680">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎Back‎‏‎‎‏‎"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎Close‎‏‎‎‏‎"</string>
<string name="available" msgid="6007778121920339498">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎Available‎‏‎‎‏‎"</string>
<string name="blocked" msgid="9195547604866033708">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎Blocked‎‏‎‎‏‎"</string>
<string name="on" msgid="280241003226755921">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎On‎‏‎‎‏‎"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎All permissions‎‏‎‎‏‎"</string>
<string name="other_permissions" msgid="2901186127193849594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎Other app capabilities‎‏‎‎‏‎"</string>
<string name="permission_request_title" msgid="8790310151025020126">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎Permission request‎‏‎‎‏‎"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎Android Wear‎‏‎‎‏‎"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎Install/Uninstall actions not supported on Wear.‎‏‎‎‏‎"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎Choose what to allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access‎‏‎‎‏‎"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎&lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; has been updated. Choose what to allow this app to access.‎‏‎‎‏‎"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎Cancel‎‏‎‎‏‎"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‏‎Always allow all‎‏‎‎‏‎"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎Ask every time‎‏‎‎‏‎"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎Don’t allow‎‏‎‎‏‎"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎Allow limited access‎‏‎‎‏‎"</string>
<string name="precise_image_description" msgid="6349638632303619872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎Precise location‎‏‎‎‏‎"</string>
<string name="approximate_image_description" msgid="938803699637069884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎Approximate location‎‏‎‎‏‎"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎Use precise location‎‏‎‎‏‎"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎When precise location is off, apps can access your approximate location‎‏‎‎‏‎"</string>
<string name="app_permission_title" msgid="2090897901051370711">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERM">%1$s</xliff:g>‎‏‎‎‏‏‏‎ permission‎‏‎‎‏‎"</string>
<string name="app_permission_header" msgid="2951363137032603806">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERM">%1$s</xliff:g>‎‏‎‎‏‏‏‎ access for this app‎‏‎‎‏‎"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERM">%1$s</xliff:g>‎‏‎‎‏‏‏‎ access for this app on ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎See all ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ permissions‎‏‎‎‏‎"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‏‎See all apps with this permission‎‏‎‎‏‎"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎Show assistant microphone usage‎‏‎‎‏‎"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎Remove permissions if app isn’t used‎‏‎‎‏‎"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎Remove permissions and free up space‎‏‎‎‏‎"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎Pause app activity if unused‎‏‎‎‏‎"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‎Manage app if unused‎‏‎‎‏‎"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎Remove permissions, delete temporary files, and stop notifications‎‏‎‎‏‎"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎Remove permissions, delete temporary files, stop notifications, and archive the app‎‏‎‎‏‎"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎To protect your data, permissions for this app will be removed if the app is unused for a few months.‎‏‎‎‏‎"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎To protect your data, if the app is unused for a few months, the following permissions will be removed: ‎‏‎‎‏‏‎<xliff:g id="PERMS">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎To protect your data, permissions have been removed from apps that you haven’t used in a few months.‎‏‎‎‏‎"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎Allowed to manage all files‎‏‎‎‏‎"</string>
<string name="ask_header" msgid="2633816846459944376">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎Ask every time‎‏‎‎‏‎"</string>
<string name="denied_header" msgid="903209608358177654">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‎‎Not allowed‎‏‎‎‏‎"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ on ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‎See more apps that can access all files‎‏‎‎‏‎"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎1 day‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎# days‎‏‎‎‏‎}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎# hour‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎# hours‎‏‎‎‏‎}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎Notes app‎‏‎‎‏‎"</string>
<string name="role_notes_description" msgid="8496852798616883551">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎Apps that allow you to take notes on your device‎‏‎‎‏‎"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‏‎notes‎‏‎‎‏‎"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎Default wallet app‎‏‎‎‏‎"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎Wallet app‎‏‎‎‏‎"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎Wallet apps can store your credit and loyalty cards, car keys and other things to help with various forms of transactions.‎‏‎‎‏‎"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎Set ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ as your default wallet app?‎‏‎‎‏‎"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎No permissions needed‎‏‎‎‏‎"</string>
<string name="request_role_current_default" msgid="738722892438247184">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‎‎‎‎Current default‎‏‎‎‏‎"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎Don’t ask again‎‏‎‎‏‎"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎Set as default‎‏‎‎‏‎"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎More defaults‎‏‎‎‏‎"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎Opening links‎‏‎‎‏‎"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎Default for work‎‏‎‎‏‎"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎Default for private space‎‏‎‎‏‎"</string>
<string name="default_app_none" msgid="9084592086808194457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎None‎‏‎‎‏‎"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‎(System default)‎‏‎‎‏‎"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎No apps‎‏‎‎‏‎"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎Show assistant trigger detection‎‏‎‎‏‎"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎Show icon in status bar when microphone is used to activate voice assistant‎‏‎‎‏‎"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos and media on your device?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos and media on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your contacts?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your contacts on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access this device’s location?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎’s&lt;/b&gt; location?‎‏‎‎‏‎"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‎‏‏‎‎The app will only have access to the location while you’re using the app‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access this device’s location?‎‏‎‎‏‎"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎’s&lt;/b&gt; location?‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎This app may want to access your location all the time, even when you’re not using the app. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎Change location access for &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎Change location access for &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎This app wants to access your location all the time, even when you’re not using the app. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to find, connect to, and determine the relative position of nearby devices? ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎Change ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>‎‏‎‎‏‏‏‎’s location access from approximate to precise?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎Change ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>‎‏‎‎‏‏‏‎’s location access on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; from approximate to precise?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access this device’s approximate location?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;’s approximate location?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎Precise‎‏‎‎‏‎"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎Approximate‎‏‎‎‏‎"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your calendar?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your calendar on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to send and view SMS messages?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to send and view SMS messages on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos, media, and files on your device?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos, media, and files on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access &lt;b&gt;photos, videos, music, and audio&lt;/b&gt; on this device?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access &lt;b&gt;photos, videos, music, audio, and other files&lt;/b&gt; on this device?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access music and audio on this device?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access music and audio on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos and videos on this device?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access photos and videos on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access more photos and videos on this device?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access more photos and videos on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎The app will only be able to record audio while you’re using the app‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio?‎‏‎‎‏‎"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to record audio on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎This app may want to record audio all the time, even when you’re not using the app. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎Change microphone access for &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‎Change microphone access for &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎This app wants to record audio all the time, even when you’re not using the app. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your physical activity?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your physical activity on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to take pictures and record video?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to take pictures and record video on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‎‏‎‏‏‎The app will only be able to take pictures and record video while you’re using the app‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to take pictures and record video?‎‏‎‎‏‎"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to take pictures and record video on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎This app may want to take pictures and record video all the time, even when you’re not using the app. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‎Change camera access for &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎Change camera access for &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎This app wants to take pictures and record video all the time, even when you’re not using the app. ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎Allow in settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your phone call logs?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access your phone call logs on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to make and manage phone calls?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to make and manage phone calls on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access sensor data about your vital signs?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access sensor data about your vital signs on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎This app wants to access sensor data about your vital signs all the time, even when you’re not using the app. To make this change, ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎go to settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access the sensor data about your vital signs?‎‏‎‎‏‎"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access the sensor data about your vital signs on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎To let this app access body sensor data all the time, even when you’re not using the app, ‎‏‎‎‏‏‎"<annotation id="link">"‎‏‎‎‏‏‏‎go to settings.‎‏‎‎‏‏‎"</annotation>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎Keep allowing &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access body sensor data while app is in use?‎‏‎‎‏‎"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‎Keep allowing &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to access body sensor data on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; while app is in use?‎‏‎‎‏‎"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to send you notifications?‎‏‎‎‏‎"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎Allow &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt; to send you notifications on &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;?‎‏‎‎‏‎"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎Controlled permissions‎‏‎‎‏‎"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ has location access‎‏‎‎‏‎"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎Your organization allows ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to access your location‎‏‎‎‏‎"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‎‏‎‎None‎‏‎‎‏‎"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎Past‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎24 hours‎‏‎‎‏‎"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‎Past‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎7 days‎‏‎‎‏‎"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="PERCENT">%2$d</xliff:g>‎‏‎‎‏‏‏‎ percent‎‏‎‎‏‎"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on the status bar or your privacy dashboard.‎‏‎‎‏‎"</string>
<string name="exempt_info_label" msgid="6286190981253476699">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on your privacy dashboard.‎‏‎‎‏‎"</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‎Device camera is blocked‎‏‎‎‏‎"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎For apps and services‎‏‎‎‏‎"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎Microphone data may still be shared when you call an emergency number.‎‏‎‎‏‎"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎Change‎‏‎‎‏‎"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎Camera access is off‎‏‎‎‏‎"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‎Microphone access is off‎‏‎‎‏‎"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎Location access is off‎‏‎‎‏‎"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎For infotainment apps‎‏‎‎‏‎"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎For required apps‎‏‎‎‏‎"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎This app is required‎‏‎‎‏‎"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎This app is required by your car’s manufacturer‎‏‎‎‏‎"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‏‎‎Security &amp; privacy‎‏‎‎‏‎"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎Scan device‎‏‎‎‏‎"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎Dismiss‎‏‎‎‏‎"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎Data sharing updates‎‏‎‎‏‎"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎Some apps changed the way they may share your location data‎‏‎‎‏‎"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎Settings‎‏‎‎‏‎"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‏‏‏‏‏‏‎Accessed ‎‏‎‎‏‏‎<xliff:g id="TIME_DATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎Accessed yesterday ‎‏‎‎‏‏‎<xliff:g id="TIME_DATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎Accessed ‎‏‎‎‏‏‎<xliff:g id="TIME_DATE_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="TIME_DATE_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎‎Your one time password is 132435‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎Restricted setting‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‏‎For your security, this setting is currently unavailable.‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‎App was denied access to ‎‏‎‎‏‏‎<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎The app requested access to a sensitive permission which can put your personal and financial info at risk.‎‏‎‎‏‏‎<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>‎‏‎‎‏‏‏‎It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=‎‏‎‎‏‏‎<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>‎‏‎‎‏‏‏‎&gt;Learn how to allow access&lt;/a&gt;‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎App was denied access to be default ‎‏‎‎‏‏‎<xliff:g id="ROLE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‎The app requested access to sensitive permissions which can put your personal and financial info at risk.‎‏‎‎‏‏‎<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>‎‏‎‎‏‏‏‎It\'s possible the app won\'t work properly without these restricted permissions. &lt;a href=‎‏‎‎‏‏‎<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>‎‏‎‎‏‏‏‎&gt;Learn how to allow access&lt;/a&gt;‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎App was denied access‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎Access to this permission can put your personal and financial info at risk.‎‏‎‎‏‏‎<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>‎‏‎‎‏‏‏‎It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=‎‏‎‎‏‏‎<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>‎‏‎‎‏‏‏‎&gt;Learn how to allow access&lt;/a&gt;‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‎Learn more‎‏‎‎‏‎"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎OK‎‏‎‎‏‎"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎‏‎Permission request suppressed‎‏‎‎‏‎"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‎‏‏‏‎This app is requesting additional permissions, but permissions can’t be granted in a streaming session. Grant the permission on your phone first.‎‏‎‎‏‎"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎For emergency call or text‎‏‎‎‏‎"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‏‎Location sent to emergency services‎‏‎‎‏‎"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. ‎‏‎‎‏‏‎"<a href="https://support.google.com/android/answer/9319337">"‎‏‎‎‏‏‏‎Learn more‎‏‎‎‏‏‎"</a>"‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
</resources>
diff --git a/PermissionController/res/values-es-rUS-v34/strings.xml b/PermissionController/res/values-es-rUS-v34/strings.xml
index e180c0b7c..31585916a 100644
--- a/PermissionController/res/values-es-rUS-v34/strings.xml
+++ b/PermissionController/res/values-es-rUS-v34/strings.xml
@@ -22,6 +22,5 @@
<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="location_settings_subtitle" msgid="6846532794702613851">"Para apps y servicios"</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>
</resources>
diff --git a/PermissionController/res/values-es-rUS-watch/strings.xml b/PermissionController/res/values-es-rUS-watch/strings.xml
index 25fa4d5f5..ce3aa4a86 100644
--- a/PermissionController/res/values-es-rUS-watch/strings.xml
+++ b/PermissionController/res/values-es-rUS-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"No puedes cambiar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sí"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancelar"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Todo el tiempo"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Con la app en uso"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Todo el tiempo"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Con la app en uso"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Todo el tiempo"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Con la app en uso"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Todo el tiempo"</string>
</resources>
diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml
index d7e550e0e..31e4cccb5 100644
--- a/PermissionController/res/values-es-rUS/strings.xml
+++ b/PermissionController/res/values-es-rUS/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string>
<string name="cancel" msgid="8943320028373963831">"Cancelar"</string>
<string name="back" msgid="6249950659061523680">"Atrás"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Cerrar"</string>
<string name="available" msgid="6007778121920339498">"Disponible"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqueado"</string>
<string name="on" msgid="280241003226755921">"Activada"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Todos los permisos"</string>
<string name="other_permissions" msgid="2901186127193849594">"Otras funciones de la app"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Solicitud de permiso"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear no admite las acciones de instalación y desinstalación"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Selecciona los permisos de acceso para &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Se actualizó &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Selecciona los permisos de acceso para esta app."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancelar"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Permitir todo siempre"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar siempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permitir el acceso limitado"</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>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Acceso a <xliff:g id="PERM">%1$s</xliff:g> para esta app en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todos los permisos de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ver todas las apps que tienen este permiso"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostrar el uso del micrófono del Asistente"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Quitar los permisos si la app no se usa"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Quitar permisos y liberar espacio"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausar actividad en la app si no se usa"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Administrar la app si no se usa"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Quitar permisos, borrar archivos temporales y detener notificaciones"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Quitar permisos, borrar archivos temporales, detener notificaciones y archivar la app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger tus datos, se quitarán los permisos de esta app si no la usas durante varios meses."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para proteger tus datos, si no usas la app durante varios meses, se quitarán los siguientes permisos: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para proteger tus datos, se quitaron los permisos de las apps que están en desuso hace varios meses."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Pueden administrar todos los archivos"</string>
<string name="ask_header" msgid="2633816846459944376">"Preguntar siempre"</string>
<string name="denied_header" msgid="903209608358177654">"Sin permiso"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver más 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>
@@ -340,17 +346,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 +368,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"App de notas"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps que te permiten tomar notas en tu dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Billetera predeterminada"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"App de billetera"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Las apps de billetera pueden almacenar tus tarjetas de crédito y lealtad, las llaves de tu vehículo y otros elementos para ayudarte con los diferentes tipos de transacciones."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"¿Quieres configurar <xliff:g id="APP_NAME">%1$s</xliff:g> como tu app de billetera predeterminada?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No se requieren permisos"</string>
<string name="request_role_current_default" msgid="738722892438247184">"App predeterminada actualmente"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"No volver a preguntar"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Hacer predeterminada"</string>
@@ -426,8 +437,9 @@
<string name="default_apps_more" msgid="4078194675848858093">"Más apps predeterminadas"</string>
<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_apps_for_private_profile" msgid="2022024112144880785">"Configuración predeterminada del espacio privado"</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 +458,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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostrar detección de activación de asistente"</string>
<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_device_aware_storage_isolated" msgid="6463062962458809752">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos y contenido multimedia en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_contacts" msgid="731025863972535928">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tus contactos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_location" msgid="6075412127429878638">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"¿Quieres cambiar el acceso a la ubicación de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"¿Quieres cambiar el acceso a la ubicación de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Esta app quiere acceder a tu ubicación todo el tiempo, incluso cuando no la uses. "<annotation id="link">"Permite el acceso 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; encuentre dispositivos cercanos, se conecte a ellos y determine su ubicación relativa?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; busque dispositivos cercanos, se conecte con ellos y determine su posición relativa en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"¿Cambiar el acceso a la ubicación de <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; de aproximada a precisa?"</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_device_aware_coarselocation" msgid="8367540370912066757">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación aproximada de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu calendario en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envíe y vea SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envíe y vea SMS en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a las fotos, el contenido multimedia y los archivos de tu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos, contenido multimedia y archivos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a &lt;b&gt;fotos, videos, música y audio&lt;/b&gt; del dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a &lt;b&gt;fotos, videos, música, audio y otros archivos&lt;/b&gt; del dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la música y los archivos de audio de este dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la música y al audio en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a las fotos y los videos de este dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos y videos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a más fotos y videos del dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a más fotos y videos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"La app solo podrá grabar audio cuando esté en uso"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Es posible que esta app quiera grabar audio todo el tiempo, incluso cuando no la estés usando. "<annotation id="link">"Permite el acceso en Configuración."</annotation></string>
<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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"¿Quieres cambiar el acceso al micrófono de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu actividad física en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tome fotos y grabe videos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tome fotos y grabe videos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"¿Cambiar el acceso a la cámara de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"¿Quieres cambiar el acceso a la cámara de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Esta app quiere tomar fotos y grabar videos todo el tiempo, incluso cuando no la uses. "<annotation id="link">"Permite el acceso en Configuración."</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 al registro de las llamadas telefónicas?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu registro de llamadas telefónicas en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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 administre las llamadas telefónicas?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga y administre llamadas telefónicas en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a datos de sensores de tus signos vitales en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"¿Quieres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a datos de sensores de tus signos vitales en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"¿Quieres seguir permitiendo que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a datos de sensores corporales en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; con la app en uso?"</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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; te envíe notificaciones en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ninguno"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Últimas\n24 horas"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Últimos\n7 días"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> por ciento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> está protegida por Android. Tus datos se procesan en este dispositivo, por eso no se muestra el uso de permisos de esta app en la barra de estado ni en el panel de privacidad."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> está protegida por Android. Tus datos se procesan en este dispositivo, por eso no se muestra el uso de permisos de esta app en el panel de privacidad."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"La cámara del dispositivo está bloqueada"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para apps y servicios"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Es posible que se sigan compartiendo los datos del micrófono cuando llames a un número de emergencia."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Cambiar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"El acceso a la cámara está desactivado"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"El acceso al micrófono está desactivado"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"El acceso a la ubicación está desactivado"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para apps de infoentretenimiento"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para apps requeridas"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Se requiere esta app"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"El fabricante del vehículo requiere esta app"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Seguridad y privacidad"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analizar dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Descartar"</string>
@@ -585,7 +632,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Último acceso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acceso: ayer a la(s) <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acceso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> a la(s) <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Tu contraseña de un solo uso es 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Parámetro restringido"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Por seguridad, este parámetro de configuración no está disponible actualmente."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"A la app se le negó el acceso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"La app solicitó acceso a un permiso sensible, lo que puede poner en riesgo tu información financiera y personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la app no funcione como corresponde sin este permiso restringido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Descubre cómo permitir el acceso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"A la app se le negó el acceso para usarse como <xliff:g id="ROLE_NAME">%1$s</xliff:g> de forma predeterminada"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"La app solicitó acceso a permisos sensibles, lo que puede poner en riesgo tu información financiera y personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la app no funcione como corresponde sin estos permisos restringidos. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Descubre cómo permitir el acceso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"A la app se le negó el acceso"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"El acceso a este permiso puede poner en riesgo tu información financiera y personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la app no funcione como corresponde sin este permiso restringido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Descubre cómo permitir el acceso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Más información"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Aceptar"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Se rechazó la solicitud de permiso"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta app solicita permisos adicionales, pero estos no se pueden otorgar durante una sesión de transmisión. Primero, otorga el permiso en el teléfono."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para mensaje de texto o llamada de emergencia"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ubicación enviada a servicios de emergencia"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta app accedió a la ubicación de tu dispositivo cuando hacías una llamada o enviabas un mensaje de texto a un número de emergencia. Esto puede suceder incluso si no se le otorgó a la app el permiso de ubicación o si la ubicación del dispositivo está desactivada. "<a href="https://support.google.com/android/answer/9319337">"Más información"</a></string>
</resources>
diff --git a/PermissionController/res/values-es-v34/strings.xml b/PermissionController/res/values-es-v34/strings.xml
index 8253d9b47..3f3c3c15e 100644
--- a/PermissionController/res/values-es-v34/strings.xml
+++ b/PermissionController/res/values-es-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Gestiona el acceso de las aplicaciones a tus datos de salud"</string>
<string name="location_settings" msgid="8863940440881290182">"Acceso a la ubicación"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Para aplicaciones y servicios. Aunque este ajuste esté desactivado, se pueden compartir datos del micrófono si llamas a un número de emergencia."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Para aplicaciones y servicios"</string>
</resources>
diff --git a/PermissionController/res/values-es-watch/strings.xml b/PermissionController/res/values-es-watch/strings.xml
index db42f6961..92bbdd057 100644
--- a/PermissionController/res/values-es-watch/strings.xml
+++ b/PermissionController/res/values-es-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"No cambiar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sí"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancelar"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Todo el tiempo"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Mientras se usa la app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Todo el tiempo"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Mientras se usa la app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Todo el tiempo"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Mientras se usa la app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Todo el tiempo"</string>
</resources>
diff --git a/PermissionController/res/values-es/strings.xml b/PermissionController/res/values-es/strings.xml
index e33a6e6dc..fdf10ee0e 100644
--- a/PermissionController/res/values-es/strings.xml
+++ b/PermissionController/res/values-es/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string>
<string name="cancel" msgid="8943320028373963831">"Cancelar"</string>
<string name="back" msgid="6249950659061523680">"Atrás"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Cerrar"</string>
<string name="available" msgid="6007778121920339498">"Disponible"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqueado"</string>
<string name="on" msgid="280241003226755921">"Activado"</string>
@@ -29,11 +30,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Todos los permisos"</string>
<string name="other_permissions" msgid="2901186127193849594">"Otras funciones de la aplicación"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Solicitud de permiso"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Las acciones de instalar y desinstalar no pueden realizarse en Wear"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Elige los permisos de acceso que quieres conceder a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; se ha actualizado. Elige los permisos de acceso que quieres conceder a esta aplicación."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancelar"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Permitir todo siempre"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar siempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"No permitir"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permitir acceso limitado"</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>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Acceso de <xliff:g id="PERM">%1$s</xliff:g> a esta aplicación en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todos los permisos de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ver todas las aplicaciones con este permiso"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostrar el uso del micrófono del Asistente"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Quitar permisos si la aplicación no se usa"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Quitar permisos y liberar espacio"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausar actividad de la aplicación si no se usa"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gestionar la aplicación si no se usa"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Quita permisos, elimina archivos temporales y detiene las notificaciones"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Quita permisos, elimina archivos temporales, detiene las notificaciones y archiva la aplicación"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger tus datos, se quitarán los permisos de esta aplicación si no la usas durante unos meses."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para proteger tus datos, si la aplicación no se ha utilizado durante unos meses, se quitarán los siguientes permisos: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para proteger tus datos, se han quitado los permisos de las aplicaciones que llevas unos meses sin usar."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Pueden gestionar todos los archivos"</string>
<string name="ask_header" msgid="2633816846459944376">"Preguntar siempre"</string>
<string name="denied_header" msgid="903209608358177654">"No permitidas"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver más aplicaciones que pueden acceder a todos los archivos"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}many{# días}other{# días}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
@@ -347,8 +353,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplicación de notas"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplicaciones que te permiten tomar notas en tu dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"App de cartera predeterminada"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplicación de cartera"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Las aplicaciones de cartera pueden almacenar tus tarjetas de crédito y de fidelización, tus llaves del coche y otros elementos para ayudarte con los distintos tipos de transacciones."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"¿Establecer <xliff:g id="APP_NAME">%1$s</xliff:g> como aplicación de cartera predeterminada?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"No se necesita ningún permiso"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Predeterminada"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"No volver a preguntar"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Establecer como predeterminado"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Más apps predeterminadas"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Abrir enlaces"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predeterminadas para trabajo"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Predeterminadas para el espacio privado"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ninguna"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Predeterminado del sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"No hay aplicaciones"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostrar la detección de activación del asistente"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostrar icono en la barra de estado cuando se utilice el micrófono para activar el asistente de voz"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a las fotos y archivos multimedia del dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos y contenido multimedia de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_contacts" msgid="731025863972535928">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los contactos de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_location" msgid="6075412127429878638">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"La aplicación solo podrá acceder a la ubicación cuando la estés usando"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"¿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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Es posible que esta aplicación quiera acceder a tu ubicación siempre, aunque no la estés usando. Puedes darle permiso en "<annotation id="link">"Ajustes"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"¿Quieres cambiar el acceso a la ubicación de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"¿Cambiar el acceso a la ubicación de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Esta aplicación quiere acceder a tu ubicación siempre, incluso aunque no la estés usando. Puedes darle permiso en "<annotation id="link">"Ajustes"</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; busque, se conecte y determine la posición relativa de dispositivos cercanos?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; busque, se conecte y fije la posición relativa de dispositivos cercanos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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; busque, se conecte y determine la posición relativa de dispositivos cercanos? "<annotation id="link">"Permítelo en los ajustes"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"¿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_device_aware_fineupgrade" msgid="4453775952305587571">"¿Cambiar el acceso a la ubicación de <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; de Aproximada a Precisa?"</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_device_aware_coarselocation" msgid="8367540370912066757">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la ubicación aproximada de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda al calendario de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envíe y lea mensajes SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envíe y lea mensajes SMS en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a las fotos, al contenido multimedia y a los archivos de tu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos, contenido multimedia y archivos de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a &lt;b&gt;fotos, vídeos, música y audio&lt;/b&gt; de este dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a &lt;b&gt;fotos, vídeos, música, audio y otros archivos&lt;/b&gt; del dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a música y audio de este dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a la música y audio de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos y vídeos de este dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos y vídeos de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a más fotos y vídeos de este dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a más fotos y vídeos de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"La aplicación solo podrá grabar audio mientras la estés usando."</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grabe audio en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Es posible que esta aplicación deba grabar audio en todo momento, aunque no se esté usando. Puedes darle este permiso en "<annotation id="link">"Ajustes."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"¿Quieres cambiar el acceso de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; al micrófono?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"¿Cambiar el acceso al micrófono de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Esta aplicación debe grabar audio en todo momento, aunque no la estés usando. "<annotation id="link">"Puedes darle este permiso en Ajustes."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"¿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_device_aware_activityRecognition" msgid="1243869530588745374">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a tu actividad física de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga fotos y grabe vídeos?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga fotos y grabe vídeos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"La aplicación solo podrá hacer fotografías y grabar vídeos mientras la estés usando."</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga fotos y grabe vídeos?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga fotos y grabe vídeos en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Es posible que esta aplicación deba hacer fotografías y grabar vídeos en todo momento, aunque no se esté usando. "<annotation id="link">"Puedes darle este permiso en Ajustes."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"¿Quieres cambiar el acceso de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; a la cámara?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"¿Cambiar el acceso a la cámara de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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_device_aware_calllog" msgid="8220927190376843309">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda al registro de llamadas telefónicas de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; haga y gestione llamadas en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de los sensores sobre tus constantes vitales de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos de los sensores sobre tus constantes vitales de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a los datos del sensor corporal de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; mientras se usa?"</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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"¿Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; te envíe notificaciones en &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nada"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Últimas\n24 horas"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Últimos\n7 días"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> por ciento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"La aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está protegida por Android. Como tus datos están procesados en este dispositivo, el uso de los permisos de esta aplicación no se muestra ni en la barra de estado ni en tu panel de privacidad."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"La aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está protegida por Android. Como tus datos están procesados en este dispositivo, el uso de los permisos de esta aplicación no se muestra en tu panel de privacidad."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"La cámara del dispositivo está bloqueada"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para aplicaciones y servicios"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Aun así, pueden compartirse datos del micrófono cuando llamas a un número de emergencia."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Cambiar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"El acceso a la cámara está desactivado"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"El acceso al micrófono está desactivado"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"El acceso a la ubicación está desactivado"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para aplicaciones de infoentretenimiento"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para aplicaciones requeridas"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Se requiere esta aplicación"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"El fabricante de tu coche requiere esta aplicación"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Seguridad y privacidad"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analizar dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Cerrar"</string>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Último acceso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acceso: ayer, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acceso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Tu contraseña de un solo uso es 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ajuste restringido"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Por seguridad, este ajuste no está disponible actualmente."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Se ha denegado el acceso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a la aplicación"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"La aplicación ha solicitado acceso a un permiso sensible que puede poner en riesgo tu información personal y financiera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la aplicación no funcione correctamente sin este permiso restringido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Consulta cómo permitir el acceso&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Se ha denegado el acceso para que la aplicación sea <xliff:g id="ROLE_NAME">%1$s</xliff:g> predeterminada"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"La aplicación ha solicitado acceso a permisos sensibles que pueden poner en riesgo tu información personal y financiera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la aplicación no funcione correctamente sin estos permisos restringidos. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Consulta cómo permitir el acceso&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Se ha denegado el acceso a la aplicación"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"El acceso a este permiso puede poner en riesgo tu información personal y financiera.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Es posible que la aplicación no funcione correctamente sin este permiso restringido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Consulta cómo permitir el acceso&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Más información"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Aceptar"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitud de permiso rechazada"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta aplicación está solicitando permisos adicionales, pero no se pueden dar durante una sesión de streaming. Da primero el permiso en tu teléfono."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para llamadas o mensajes de emergencia"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ubicación enviada a los servicios de emergencias"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta aplicación ha accedido a la ubicación de tu dispositivo mientras hacías una llamada o enviabas un mensaje al número de emergencia. Esto puede ocurrir aunque la aplicación no tenga el permiso de ubicación o la ubicación del dispositivo esté desactivada. "<a href="https://support.google.com/android/answer/9319337">"Más información"</a></string>
</resources>
diff --git a/PermissionController/res/values-et-v34/strings.xml b/PermissionController/res/values-et-v34/strings.xml
index 4476aecdd..2db42fd0e 100644
--- a/PermissionController/res/values-et-v34/strings.xml
+++ b/PermissionController/res/values-et-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Saate hallata rakenduse juurdepääsu terviseandmetele"</string>
<string name="location_settings" msgid="8863940440881290182">"Juurdepääs asukohale"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Rakenduste ja teenuste jaoks. Isegi kui see seade on välja lülitatud, võidakse mikrofoni andmeid siiski jagada hädaabinumbrile helistades."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Rakenduste ja teenuste jaoks"</string>
</resources>
diff --git a/PermissionController/res/values-et-watch/strings.xml b/PermissionController/res/values-et-watch/strings.xml
index b96aab80f..9813469ea 100644
--- a/PermissionController/res/values-et-watch/strings.xml
+++ b/PermissionController/res/values-et-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ei saa muuta"</string>
<string name="generic_yes" msgid="2489207724988649846">"Jah"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Tühista"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Kogu aeg"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Rakenduse kasutamise ajal"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Kogu aeg"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Rakenduse kasutamise ajal"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Kogu aeg"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Rakenduse kasutamise ajal"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Kogu aeg"</string>
</resources>
diff --git a/PermissionController/res/values-et/strings.xml b/PermissionController/res/values-et/strings.xml
index 027b554e0..24d801411 100644
--- a/PermissionController/res/values-et/strings.xml
+++ b/PermissionController/res/values-et/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"load"</string>
<string name="cancel" msgid="8943320028373963831">"Tühista"</string>
<string name="back" msgid="6249950659061523680">"Tagasi"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Sule"</string>
<string name="available" msgid="6007778121920339498">"Saadaval"</string>
<string name="blocked" msgid="9195547604866033708">"Blokeeritud"</string>
<string name="on" msgid="280241003226755921">"Sees"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Kõik load"</string>
<string name="other_permissions" msgid="2901186127193849594">"Rakenduse muud funktsioonid"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Loa taotlus"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear ei toeta installimist/desinstallimist."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Valige, millele lubate rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurde pääseda"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Rakendust &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; värskendati. Valige, millele lubate sellel rakendusel juurde pääseda."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Tühista"</string>
@@ -191,20 +192,24 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Luba alati kõik"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Küsi iga kord"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ära luba"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Luba piiratud juurdepääs"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Täpne asukoht"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Ligikaudne asukoht"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Täpse asukoha kasutamine"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kui täpne asukoht on välja lülitatud, pääsevad rakendused juurde teie ligikaudsele asukohale"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Funktsiooni <xliff:g id="PERM">%1$s</xliff:g> luba"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Rakenduse juurdepääs funktsioonile <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> juurdepääs sellele rakendusele seadmes <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Kuva rakenduse <xliff:g id="APP">%1$s</xliff:g> kõik load"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Kuva kõik selle loaga rakendused"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Kuva assistendi mikrofoni kasutamine"</string>
<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_label_v3" msgid="693340578642156657">"Halda kasutamata rakendusi"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Eemaldatakse load, kustutatakse ajutised failid ja peatatakse märguanded"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Eemalda load, kustuta ajutised failid, peata märguanded ja arhiivi rakendus"</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>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Teie andmete kaitsmiseks eemaldati load rakendustelt, mida te ei ole mõne kuu jooksul kasutanud."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Luba kõikide failide haldamiseks"</string>
<string name="ask_header" msgid="2633816846459944376">"Küsi iga kord"</string>
<string name="denied_header" msgid="903209608358177654">"Pole lubatud"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> seadmes <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Kuva rohkem rakendusi, mis kõigile failidele juurde pääsevad"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 päev}other{# päeva}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# tund}other{# tundi}}"</string>
@@ -361,7 +367,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Märkmerakendus"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Rakendused, mis võimaldavad teie seadmes märkmeid teha"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"märkmed"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Vaikerahakotirakendus"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Rahakotirakendus"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Rahakotirakendused võivad salvestada teie krediit- ja kliendikaarte, autovõtmeid ning muid asju, et eri tehinguvormide puhul aidata."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Kas määrata <xliff:g id="APP_NAME">%1$s</xliff:g> vaikimisi teie rahakotirakenduseks?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Lube ei ole vaja"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Praegune vaikeseade"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ära enam küsi"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Määra vaikeseadeks"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Rohkem vaikeseadeid"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Linkide avamine"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Vaikerakendused töö jaoks"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Privaatse ruumi vaikerakendused"</string>
<string name="default_app_none" msgid="9084592086808194457">"Puudub"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Süsteemi vaikeseade)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Rakendusi pole"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Assistendi käivitamise tuvastamise kuvamine"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Ikooni kuvamine olekuribal, kui häälassistendi aktiveerimiseks kasutatakse mikrofoni"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs seadmes olevatele fotodele ja meediale?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs fotodele ja meediale?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs teie kontaktidele?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs teie kontaktidele?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs selle seadme asukohale?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs seadme &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; asukohale?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Rakendusel on juurdepääs asukohale vaid sel ajal, kui rakendust kasutate"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs selle seadme asukohale?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs seadme &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; asukohale?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"See rakendus võib soovida pidevat juurdepääsu teie asukohale (ka siis, kui te rakendust ei kasuta). "<annotation id="link">"Lubage see seadetes"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Kas muuta rakenduse &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhul juurdepääsu asukohale?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Kas muuta rakenduse &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhul seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääsu asukohale?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"See rakendus soovib pidevat juurdepääsu teie asukohale (ka siis, kui te rakendust ei kasuta). "<annotation id="link">"Lubage see seadetes"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; leida lähedalasuvaid seadmeid, nendega ühendada ja nende suhteline asukoht määrata?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; otsida ja määratleda seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; läheduses olevate seadmete suhtelist asukohta ja sellega ühendada?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; leida lähedalasuvaid seadmeid, nendega ühendada ja nende suhteline asukoht määrata? "<annotation id="link">"Lubage menüüs Seaded."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Kas muuta rakenduse <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> asukohale juurdepääsemise tase ligikaudsest täpseks?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Kas muuta rakenduse <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> juurdepääs asukohateabele seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ligikaudsest täpseks?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs selle seadme ligikaudsele asukohale?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs seadme &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ligikaudsele asukohale?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Täpne"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Ligikaudne"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs teie kalendrile?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs teie kalendrile?"</string>
<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_device_aware_sms" msgid="6639977653040502291">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs 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_device_aware_read_media_aural" msgid="7927884506238101064">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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_device_aware_read_media_visual" msgid="3122576538319059333">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs rohkematele fotodele ja videotele?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; salvestada heli?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; salvestada seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; heli?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Rakendus saab heli salvestada vaid siis, kui rakendust kasutate"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; heli salvestada?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; salvestada seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; heli?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"See rakendus võib soovida heli salvestada mis tahes ajal (ka siis, kui te rakendust ei kasuta). "<annotation id="link">"Lubage see seadetes."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Kas muuta rakenduse &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhul juurdepääsu mikrofonile?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Kas muuta rakenduse &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhul seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääsu mikrofonile?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"See rakendus soovib heli salvestada mis tahes ajal (ka siis, kui te rakendust ei kasuta). "<annotation id="link">"Lubage see seadetes."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Kas anda rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; juurdepääs teie füüsilisele tegevusele?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs teie füüsilise tegevuse andmetele?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; jäädvustada pilte ja salvestada videoid?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; teha seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; pilte ja salvestada videot?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Rakendus saab pildistada ja videoid salvestada vaid siis, kui rakendust kasutate"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pildistada ja videoid salvestada?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; teha seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; pilte ja salvestada videot?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"See rakendus võib soovida pildistada ja videoid salvestada mis tahes ajal (ka siis, kui te rakendust ei kasuta). "<annotation id="link">"Lubage see seadetes."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Kas muuta rakenduse &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhul juurdepääsu kaamerale?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Kas muuta rakenduse &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhul seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääsu kaamerale?"</string>
<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_device_aware_calllog" msgid="8220927190376843309">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; juurdepääs 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_device_aware_phone" msgid="590399263670349955">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; helistada ja telefonikõnesid hallata?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Kas lubada rakendusele &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; rakenduse kasutamise ajal jätkuvalt kehaanduri andmetele juurde pääseda?"</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>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Kas lubada rakendusel &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; seadmes &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; teile märguandeid saata?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Hallatud load"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> pääseb juurde asukohale"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Teie organisatsioon lubab rakendusel <xliff:g id="APP_NAME">%1$s</xliff:g> pääseda juurde teie asukohale."</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Puudub"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Viimased\n24 tundi"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Viimased\n7 päeva"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> protsenti"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> kaitseb Android. Kuna teie andmeid töödeldakse selles seadmes, ei kuvata selle rakenduse lubade kasutust olekuribal ega teie privaatsuse juhtpaneelil."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> kaitseb Android. Kuna teie andmeid töödeldakse selles seadmes, ei kuvata selle rakenduse lubade kasutust teie privaatsuse juhtpaneelil."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Seadme kaamera on blokeeritud"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Rakenduste ja teenuste jaoks"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofoni andmeid võidakse siiski jagada hädaabinumbrile helistades."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Muuda"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Juurdepääs kaamerale on välja lülitatud"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Juurdepääs mikrofonile on välja lülitatud"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Juurdepääs asukohale on välja lülitatud"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Meelelahutussüsteemi rakenduste puhul"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Nõutavate rakenduste puhul"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"See rakendus on nõutav"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Seda rakendust nõuab teie auto tootja"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Turvalisus ja privaatsus"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skanni seadet"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Loobu"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Andmete jagamise värskendused"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Mõni rakendus on muutnud teie asukohaandmete jagamise viisi"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Seaded"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Juurde pääsetud <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Juurde pääsetud eile <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Juurde pääsetud <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Teie ühekordne parool on 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Piiratud seade"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Teie turvalisuse huvides pole see seade praegu saadaval."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Rakendusele ei antud luba <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Rakendus taotles tundlikku luba, mis võib teie isikuandmed ja finantsteabe ohtu seada.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Võimalik, et rakendus ei tööta ilma selle piiratud loata korralikult. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt; Teave juurdepääsu andmise kohta&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Rakendusele ei antud luba olla vaikimisi <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Rakendus taotles tundlikke lubasid, mis võivad teie isikuandmed ja finantsteabe ohtu seada.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Võimalik, et rakendus ei tööta ilma nende piiratud lubadeta korralikult. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt; Teave loa andmise kohta&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Rakendusele ei antud luba"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Selle loa andmine võib teie isikuandmed ja finantsteabe ohtu seada.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Võimalik, et rakendus ei tööta ilma selle piiratud loata korralikult. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt; Teave loa andmise kohta&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Lisateave"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Loataotlus peideti"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"See rakendus taotleb lisalube, kuid lube ei saa voogesituse seansis anda. Kõigepealt andke luba oma telefonile."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Hädaabikõne või hädaabinumbrile tekstsõnumi saatmise jaoks"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Asukoht saadeti hädaabiteenustele"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"See rakendus pääses hädaabinumbrile helistamise või sõnumi saatmise käigus juurde teie seadme asukohale. See võib juhtuda ka siis, kui rakendusel pole asukohale juurdepääsu luba või seadme asukoht on välja lülitatud. "<a href="https://support.google.com/android/answer/9319337">"Lisateave"</a></string>
</resources>
diff --git a/PermissionController/res/values-eu-v34/strings.xml b/PermissionController/res/values-eu-v34/strings.xml
index 5b1882738..d1921fe84 100644
--- a/PermissionController/res/values-eu-v34/strings.xml
+++ b/PermissionController/res/values-eu-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Kudeatu aplikazioak osasunari buruzko datuak erabiltzeko duen baimena"</string>
<string name="location_settings" msgid="8863940440881290182">"Kokapena erabiltzeko baimena"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Aplikazio eta zerbitzuetarako. Ezarpena desaktibatuta badago ere, baliteke mikrofonoaren bidez lortutako datuak partekatzea larrialdietarako zenbaki batera deitzean."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Aplikazio eta zerbitzuetarako"</string>
</resources>
diff --git a/PermissionController/res/values-eu-watch/strings.xml b/PermissionController/res/values-eu-watch/strings.xml
index 840f6faef..0617758e9 100644
--- a/PermissionController/res/values-eu-watch/strings.xml
+++ b/PermissionController/res/values-eu-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ezin da aldatu"</string>
<string name="generic_yes" msgid="2489207724988649846">"Bai"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Utzi"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Beti"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Aplikazioa erabili bitartean"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Beti"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Aplikazioa erabili bitartean"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Beti"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Aplikazioa erabili bitartean"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Beti"</string>
</resources>
diff --git a/PermissionController/res/values-eu/strings.xml b/PermissionController/res/values-eu/strings.xml
index 77bb21163..025467c8a 100644
--- a/PermissionController/res/values-eu/strings.xml
+++ b/PermissionController/res/values-eu/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"baimenak"</string>
<string name="cancel" msgid="8943320028373963831">"Utzi"</string>
<string name="back" msgid="6249950659061523680">"Atzera"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Itxi"</string>
<string name="available" msgid="6007778121920339498">"Baimenduta"</string>
<string name="blocked" msgid="9195547604866033708">"Blokeatuta"</string>
<string name="on" msgid="280241003226755921">"Aktibatuta"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Baimen guztiak"</string>
<string name="other_permissions" msgid="2901186127193849594">"Aplikazioaren beste gaitasun batzuk"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Baimen-eskaera"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Instalatzeko eta desinstalatzeko ekintzak ezin dira gauzatu Wear gailuetan."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Aukeratu zer atzi dezakeen &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioak"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Eguneratu egin da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Aukeratu aplikazioak zer atzi dezakeen."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Utzi"</string>
@@ -150,7 +151,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 +173,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>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Eman beti baliabide guztiak erabiltzeko baimena"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Galdetu beti"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ez eman baimenik"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Eman sarbide mugatua"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Kokapen zehatza"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Gutxi gorabeherako kokapena"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Erabili kokapen zehatza"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kokapen zehatza desaktibatuta dagoenean, aplikazioek gutxi gorabeherako kokapena atzi dezakete"</string>
<string name="app_permission_title" msgid="2090897901051370711">"\"<xliff:g id="PERM">%1$s</xliff:g>\" baimena"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Aplikazio honek \"<xliff:g id="PERM">%1$s</xliff:g>\" erabiltzeko duen baimena"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> erabiltzeko baimena aplikazio honetarako <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuan"</string>
<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>
@@ -204,7 +207,9 @@
<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>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Kudeatu aplikazioa erabiltzen ez bada"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Kendu baimenak, ezabatu aldi baterako fitxategiak eta geldiarazi jakinarazpenak"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Kendu baimenak, ezabatu aldi baterako fitxategiak, geldiarazi jakinarazpenak eta artxibatu aplikazioa"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Zure datuak babeste aldera, aplikazio honen baimenak kendu egingo dira aplikazioa ez baduzu erabiltzen zenbait hilabetez."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Zure datuak babeste aldera, kendu egingo dira honako baimen hauek zenbait hilabetez aplikazioa erabiltzen ez baduzu: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Zure datuak babeste aldera, kendu egin dira zenbait hilabetez erabili ez dituzun aplikazioen baimenak."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Fitxategi guztiak kudeatzeko baimena dutenak"</string>
<string name="ask_header" msgid="2633816846459944376">"Galdetu beti"</string>
<string name="denied_header" msgid="903209608358177654">"Baimendu gabekoak"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ikusi fitxategi guztiak atzi ditzaketen aplikazio gehiago"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 egun}other{# egun}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ordu}other{# ordu}}"</string>
@@ -352,7 +358,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 +367,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 +381,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Oharren aplikazioa"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Gailuan oharrak idazteko aukera ematen dizuten aplikazioak"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"oharrak"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Diru-zorro lehenetsia"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Diru-zorroaren aplikazioa"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Diru-zorroaren aplikazioetan zure saldoa eta fideltasun-txartelak, autoko giltzak nahiz bestelako gauzak gorde ditzakezu, errazagoa izan dadin askotariko transakzioak egitea."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> diru-zorro lehenetsi gisa ezarri nahi duzu?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Ez du behar baimenik"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Aplikazio lehenetsia"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ez galdetu berriro"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Ezarri lehenetsi gisa"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Aplikazio lehenetsi gehiago"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Irekiko diren estekak"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Lanerako aplikazio lehenetsiak"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Eremu pribatuko aplikazio lehenetsiak"</string>
<string name="default_app_none" msgid="9084592086808194457">"Bat ere ez"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(sistemaren aplikazio lehenetsia)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Ez dago aplikaziorik"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Erakutsi laguntzailea abiarazteko hautematea"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mikrofonoa erabiltzen denean ahozko laguntza aktibatzeko, erakutsi dagokion ikonoa egoera-barran"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Gailuko argazkiak eta multimedia-edukia erabiltzeko baimena eman nahi diozu &lt;b&amp;gt<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko argazkiak eta multimedia-edukia 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_contacts" msgid="8391550064551053695">"Kontaktuak 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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko kontaktuak 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_location" msgid="6990232580121067883">"Gailuaren kokapena 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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuaren kokapena erabiltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Hura erabiltzen ari zarenean soilik atzituko du aplikazioak kokapena"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Gailuaren kokapena erabiltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuaren kokapena 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_location" msgid="8021219324989662957">"Baliteke aplikazioak beti atzitu behar izatea zure kokapena, baita aplikazioa erabiltzen ari ez zarenean ere. "<annotation id="link">"Eman baimen hori Ezarpenak atalean"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Kokapenerako sarbidea aldatu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuaren kokapena erabiltzeko baimena aldatu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Aplikazioak beti atzitu nahi du zure kokapena, baita aplikazioa erabiltzen ari ez zarenean ere. "<annotation id="link">"Eman baimen hori Ezarpenak atalean"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Inguruko gailuak aurkitu, haietara konektatu eta haien arteko distantzia erlatiboa zehazteko baimena eman &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuan inguruko gailuak aurkitu, haietara konektatu eta haien distantzia erlatiboa zehazteko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Inguruko gailuak aurkitzeko, haietara konektatzeko eta haien arteko distantzia erlatiboa zehazteko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;gt aplikazioari? "<annotation id="link">"Eman baimena ezarpenetan."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> aplikazioak gutxi gorabeherako kokapena atzi dezake. Kokapen zehatza erabiltzeko baimena eman nahi diozu?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> aplikazioak &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuaren gutxi gorabeherako kokapena erabil dezake. Kokapen zehatza erabiltzeko baimena eman nahi diozu?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Gailuaren gutxi gorabeherako kokapena 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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuaren gutxi gorabeherako kokapena 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_finelocation_imagetext" msgid="1313062433398914334">"Zehatza"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Gutxi gorabeherakoa"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Egutegia 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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko egutegia 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_sms" msgid="5672063688745420991">"SMS mezuak bidaltzeko eta ikusteko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez SMS mezuak bidaltzeko eta ikusteko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Gailuko argazkiak, multimedia-edukia eta fitxategiak 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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko argazkiak, multimedia-edukia eta fitxategiak 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_storage_q_to_s" msgid="8213701872983685505">"Gailuko &lt;b&gt;argazkiak, bideoak, musika eta audioa&lt;/b&gt; 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_storage_pre_q" msgid="168130651144569428">"Gailuko &lt;b&gt;argazkiak, bideoak, musika, audioa eta bestelako fitxategiak&lt;/b&gt; 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_read_media_aural" msgid="2593365397347577812">"Gailuko musika eta audioa 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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko musika eta audioa 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_read_media_visual" msgid="5548780620779729975">"Gailuko argazkiak eta bideoak 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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko argazki eta bideoak 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_more_photos" msgid="128933814654231321">"Gailuko argazki eta bideo gehiago 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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko argazki eta bideo gehiago atzitzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikazioak hura erabiltzean soilik grabatuko du audioa"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez audioa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Baliteke aplikazioak edonoiz grabatzea audioa, baita aplikazioa erabiltzen ari ez zarenean ere. "<annotation id="link">"Eman baimena ezarpenetan."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Mikrofonorako sarbidea aldatu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko mikrofonoa erabiltzeko baimena aldatu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Aplikazioak edonoiz grabatu nahi du audioa, baita aplikazioa erabiltzen ari ez zarenean ere. "<annotation id="link">"Eman baimena ezarpenetan."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Zure jarduera fisikoa 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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko jarduera fisikoak 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_camera" msgid="5123097035410002594">"Argazkiak ateratzeko eta bideoak grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez argazkiak ateratzeko eta bideoa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikazioak hura erabiltzean soilik aterako ditu argazkiak, eta grabatuko bideoak"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Argazkiak atera eta bideoak grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez argazkiak ateratzeko eta bideoa grabatzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Baliteke aplikazioak edonoiz ateratzea argazkiak eta grabatzea bideoak, baita aplikazioa erabiltzen ari ez zarenean ere. "<annotation id="link">"Eman baimena ezarpenetan."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Kamerarako sarbidea aldatu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko kamera erabiltzeko baimena aldatu nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Aplikazioak edonoiz atera nahi ditu argazkiak eta grabatu bideoak, baita aplikazioa erabiltzen ari ez zarenean ere. "<annotation id="link">"Eman baimena ezarpenetan."</annotation></string>
<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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko telefono-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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez 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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko 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="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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko 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>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; erabili bitartean aplikazio horri &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; gailuko gorputz-sentsoreen datuak erabiltzeko baimena ematen jarraitu nahi duzu?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Jakinarazpenak bidaltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; bidez jakinarazpenak bidaltzeko baimena eman nahi diozu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aplikazioari?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrolatutako baimenak"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak kokapena erabil dezake"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Erakundeak kokapena erabiltzeko baimena eman dio <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioari"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Bat ere ez"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Azken\n24 orduak"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Azken\nzazpi egunak"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ehuneko <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Android-ek babesten du <xliff:g id="APP_NAME">%1$s</xliff:g>. Datuak gailuan prozesatzen direnez, aplikazioaren baimenen erabilera ez da agertzen ez egoera-barran ezta pribatutasun-panelean ere."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Android-ek babesten du <xliff:g id="APP_NAME">%1$s</xliff:g>. Datuak gailuan prozesatzen direnez, aplikazioaren baimenen erabilera ez da agertzen pribatutasun-panelean."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Gailuaren kamera blokeatuta dago"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Aplikazio eta zerbitzuetarako"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Agian mikrofonotik lortutako datuak larrialdietarako zenbaki batera deitzen duzunean partekatuko dira."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Aldatu"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kamera erabiltzeko baimena desaktibatuta dago"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofonoa erabiltzeko baimena desaktibatuta dago"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Kokapena erabiltzeko baimena desaktibatuta dago"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Informazio- eta aisia-aplikazioetarako"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Beharrezko aplikazioetarako"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Aplikazio hau beharrezkoa da"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Autoaren fabrikatzaileak aplikazio hau erabiltzea eskatzen du"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Segurtasuna eta pribatutasuna"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Aztertu gailua"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Baztertu"</string>
@@ -582,7 +629,7 @@
<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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Datuak partekatzeko aukeraren berritasunak"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Kokapen-datuak partekatzeko modua aldatu dute aplikazio batzuek"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Ezarpenak"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Atzitze-data: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Atzo atzitu zen (<xliff:g id="TIME_DATE">%1$s</xliff:g>)"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Atzitze-data: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> (<xliff:g id="TIME_DATE_1">%2$s</xliff:g>)"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Zure erabilera bakarreko pasahitza 132435 da"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Murriztapenak ditu ezarpenak"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Zure segurtasuna bermatzeko, ezarpena ez dago erabilgarri une honetan."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikazioari <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> erabiltzeko baimena ukatu zaio"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Kontuzko informazioa erabiltzeko baimen bat eskatu du aplikazioak, eta agian horrek arriskuan jarriko ditu zure informazio pertsonala eta finantzei buruzko informazioa.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Baliteke aplikazioak behar bezala ez funtzionatzea baimen murriztu hori gabe. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lortu baimena emateko argibideak&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikazioari <xliff:g id="ROLE_NAME">%1$s</xliff:g> lehenetsia izateko baimena ukatu zaio"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Kontuzko informazioa erabiltzeko baimen batzuk eskatu ditu aplikazioak, eta agian horrek arriskuan jarriko ditu zure informazio pertsonala eta finantzei buruzko informazioa.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Baliteke aplikazioak behar bezala ez funtzionatzea baimen murriztu horiek gabe. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lortu baimena emateko argibideak&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikazioari baimena ukatu zaio"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Baimen hau emanez gero, agian arriskuan jarriko dira zure informazio pertsonala eta finantzei buruzko informazioa.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Baliteke aplikazioak behar bezala ez funtzionatzea baimen murriztu hori gabe. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lortu baimena emateko argibideak&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Lortu informazio gehiago"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Ados"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Blokeatu da baimen-eskaera"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Baimen gehigarriak eskatzen ari da aplikazioa, baina ezin da eman baimenik igorpen-saioetan. Lehenik eta behin, eman baimena telefonoan."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Larrialdi-deiak egitean edo larrialdietarako testu-mezuak bidaltzean"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Kokapena larrialdi-zerbitzuei bidali zaie"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Aplikazioak gailuaren kokapena atzitu du larrialdietarako zenbaki batera dei bat egin edo testu-mezu bat bidali duzun bitartean. Baliteke aplikazioak kokapena erabiltzeko baimena ez izan edo gailuaren kokapena desaktibatuta egon arren gertatzea hori. "<a href="https://support.google.com/android/answer/9319337">"Lortu informazio gehiago"</a></string>
</resources>
diff --git a/PermissionController/res/values-fa-v34/strings.xml b/PermissionController/res/values-fa-v34/strings.xml
index de8036122..386c748dc 100644
--- a/PermissionController/res/values-fa-v34/strings.xml
+++ b/PermissionController/res/values-fa-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-fa-watch/strings.xml b/PermissionController/res/values-fa-watch/strings.xml
index 17b225913..b602b38f3 100644
--- a/PermissionController/res/values-fa-watch/strings.xml
+++ b/PermissionController/res/values-fa-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"امکان تغییر نیست"</string>
<string name="generic_yes" msgid="2489207724988649846">"بله"</string>
<string name="generic_cancel" msgid="2631708607129269698">"لغو"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"همیشه"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"درحین استفاده از برنامه"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"همیشه"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"درحین استفاده از برنامه"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"همیشه"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"درحین استفاده از برنامه"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"همیشه"</string>
</resources>
diff --git a/PermissionController/res/values-fa/strings.xml b/PermissionController/res/values-fa/strings.xml
index a382eb3ae..6165741d1 100644
--- a/PermissionController/res/values-fa/strings.xml
+++ b/PermissionController/res/values-fa/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"اجازه‌ها"</string>
<string name="cancel" msgid="8943320028373963831">"لغو"</string>
<string name="back" msgid="6249950659061523680">"برگشت"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"بستن"</string>
<string name="available" msgid="6007778121920339498">"دردسترس"</string>
<string name="blocked" msgid="9195547604866033708">"مسدود"</string>
<string name="on" msgid="280241003226755921">"روشن"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"‏کنش‌های نصب/حذف نصب در Wear پشتیبانی نمی‌شود."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"‏انتخاب کنید &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه دارد به چه چیزی دسترسی پیدا کند"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; به‌روزرسانی شده است. انتخاب کنید این برنامه اجازه دارد به چه چیزی دسترسی پیدا کند."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"لغو"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"مجاز کردن دسترسی محدود"</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_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_with_device_name" msgid="7193042925656173271">"دسترسی <xliff:g id="PERM">%1$s</xliff:g> از این برنامه در <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"مدیریت برنامه درصورت عدم استفاده"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"اجازه‌ها برداشته می‌شود، فایل‌های موقت حذف می‌شود، و اعلان‌ها متوقف می‌شوند"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"برداشتن اجازه‌ها، حذف فایل‌های موقت، توقف اعلان‌ها، و بایگانی کردن برنامه"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"برای محافظت از داده‌هایتان، اگر طی چند ماه گذشته از این برنامه استفاده نشده باشد، اجازه‌های آن برداشته خواهد شد."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"برای محافظت از داده‌هایتان، اگر طی چند ماه گذشته از این برنامه استفاده نشده باشد، اجازه‌های زیر برداشته خواهد شد: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"برای محافظت از داده‌هایتان، اجازه‌ها از برنامه‌هایی که طی چند ماه گذشته استفاده نشده‌اند برداشته شده است."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"مجاز برای مدیریت همه فایل‌ها"</string>
<string name="ask_header" msgid="2633816846459944376">"هربار پرسیده شود"</string>
<string name="denied_header" msgid="903209608358177654">"مجاز نبودن"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> در <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"دیدن برنامه‌های دیگری که به همه فایل‌ها دسترسی دارند"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{یک روز}one{# روز}other{# روز}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ساعت}one{# ساعت}other{# ساعت}}"</string>
@@ -260,11 +266,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 +287,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 +302,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"برنامه کیف پول پیش‌فرض"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"برنامه کیف پول"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"برنامه‌های کیف پول می‌تواند با ذخیره کردن کارت‌های اعتباری و وفاداری، کلید خودرو، و موارد دیگر در انجام انواع مختلف تراکنش به شما کمک کند."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> به‌عنوان برنامه کیف پول پیش‌فرض تنظیم شود؟"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"اجازه‌ای لازم نیست"</string>
<string name="request_role_current_default" msgid="738722892438247184">"برنامه پیش‌فرض کنونی"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"دوباره سؤال نشود"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"تنظیم برای پیش‌فرض"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"سایر پیش‌فرض‌ها"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"باز کردن پیوندها"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"پیش‌فرض برای کار"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"برنامه‌های پیش‌فرض برای فضای خصوصی"</string>
<string name="default_app_none" msgid="9084592086808194457">"هیچ‌کدام"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(پیش‌فرض سیستم)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"برنامه‌ای موجود نیست"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به عکس‌ها و رسانه‌های «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_contacts" msgid="731025863972535928">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به مخاطبین شما در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_location" msgid="6075412127429878638">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به مکان «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_location" msgid="1264484517831380016">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به مکان «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"دسترسی «<xliff:g id="APP_NAME">%1$s</xliff:g>» به مکان در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» تغییر کند؟"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"این برنامه می‌خواهد مرتب به مکانتان دسترسی داشته باشد، حتی زمانی‌که از برنامه استفاده نمی‌کنید. "<annotation id="link">"در تنظیمات به آن اجازه دهید."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه داده شود دستگاه‌های اطراف را پیدا کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را مشخص کند؟"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏به &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اجازه می‌دهید دستگاه‌های اطراف را پیدا کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را مشخص کند؟"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دستگاه‌های اطراف را پیدا کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را تعیین کند؟"</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_device_aware_fineupgrade" msgid="4453775952305587571">"دسترسی <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> به مکان «<xliff:g id="DEVICE_NAME">%2$s</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_device_aware_coarselocation" msgid="8367540370912066757">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به مکان تقریبی «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_calendar" msgid="7161929851377463612">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به تقویمتان در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_sms" msgid="6639977653040502291">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه ارسال و مشاهده پیامک در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» را می‌دهید؟"</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_device_aware_storage" msgid="6933251810928606636">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به عکس‌ها، رسانه‌ها، و فایل‌های «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_read_media_aural" msgid="7927884506238101064">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» به فایل‌های موسیقی و صوتی دسترسی داشته باشد؟"</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_device_aware_read_media_visual" msgid="3122576538319059333">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به عکس‌ها و ویدیوهای «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_more_photos" msgid="1703469013613723053">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به عکس‌ها و ویدیوهای بیشتری در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_microphone" msgid="8821701550505437951">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» صدا ضبط کند؟"</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="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» صدا ضبط کند؟"</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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"دسترسی «<xliff:g id="APP_NAME">%1$s</xliff:g>» به میکروفون در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» تغییر کند؟"</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_device_aware_activityRecognition" msgid="1243869530588745374">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه دسترسی به فعالیت فیزیکی‌تان در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» را می‌دهید؟"</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_device_aware_camera" msgid="5340173564041615494">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» عکس بگیرد و ویدیو ضبط کند؟"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه عکس گرفتن و ویدیو ضبط کردن در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» را می‌دهید؟"</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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"دسترسی «<xliff:g id="APP_NAME">%1$s</xliff:g>» به دوربین در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» تغییر کند؟"</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_device_aware_calllog" msgid="8220927190376843309">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به گزارش‌های تماس تلفنی‌تان در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_phone" msgid="590399263670349955">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» تماس تلفنی برقرار کند و آن‌ها را مدیریت کند؟"</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_device_aware_sensors" msgid="3874451050573615157">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به داده‌های حسگر مربوط به علائم حیاتی‌تان در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» دسترسی داشته باشد؟"</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_device_aware_sensors" msgid="3687673359121603824">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید به داده‌های حسگر مربوط به علائم حیاتی‌تان در «<xliff:g id="DEVICE_NAME">%2$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">"‏به &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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"همچنان به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید وقتی برنامه درحال استفاده است به داده‌های حسگر بدن در «<xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"به «<xliff:g id="APP_NAME">%1$s</xliff:g>» اجازه می‌دهید در «<xliff:g id="DEVICE_NAME">%2$s</xliff:g>» اعلان ارسال کند؟"</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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"هیچ‌کدام"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"۲۴\nساعت گذشته"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"۷ روز\nگذشته"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> درصد"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"‏<xliff:g id="APP_NAME">%1$s</xliff:g> تحت حفاظت Android است. چون داده‌هایتان در این دستگاه پردازش می‌شود، استفاده این برنامه از اجازه در نوار وضعیت یا داشبورد حریم خصوصی‌تان نشان داده نمی‌شود."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"دسترسی به دوربین خاموش است"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"دسترسی به میکروفون خاموش است"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"دسترسی به مکان خاموش است"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"برای برنامه‌های اطلاعات-سرگرمی"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"برای برنامه‌های الزامی"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"این برنامه الزامی است"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"سازنده خودرو این برنامه را الزامی کرده است"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"تاریخ دسترسی: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"دسترسی در روز گذشته ساعت <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"دسترسی در <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ساعت <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"گذرواژه یکبارمصرف شما 132435 است"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"تنظیم محدودشده"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"برای حفظ امنیت شما، درحال‌حاضر این تنظیم دردسترس نیست."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"درخواست برنامه برای دسترسی به <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> رد شد"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏این برنامه درخواست دسترسی به اجازه‌ای حساس را داشته است که می‌تواند اطلاعات شخصی و مالی‌تان را درمعرض خطر قرار دهد.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ممکن است برنامه بدون این اجازه محدودشده به‌درستی کار نکند. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;با نحوه اعطای دسترسی آشنا شوید&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"درخواست دسترسی برنامه برای تبدیل شدن به <xliff:g id="ROLE_NAME">%1$s</xliff:g> پیش‌فرض رد شد"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"‏این برنامه درخواست دسترسی به اجازه‌های حساسی را داشته است که می‌تواند اطلاعات شخصی و مالی‌تان را درمعرض خطر قرار دهد.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ممکن است برنامه بدون این اجازه‌های محدودشده به‌درستی کار نکند. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;با نحوه اعطای دسترسی آشنا شوید&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"درخواست دسترسی برنامه رد شد"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"‏اعطای این اجازه می‌تواند اطلاعات شخصی و مالی‌تان را درمعرض خطر قرار دهد.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ممکن است برنامه بدون این اجازه محدودشده به‌درستی کار نکند. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;با نحوه اعطای دسترسی آشنا شوید&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"بیشتر بدانید"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"بسیارخوب"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"درخواست اجازه رد شد"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"این برنامه درخواست اجازه‌های اضافی دارد، اما امکان دادن این اجازه‌ها در جلسه جاری‌سازی وجود ندارد. ابتدا اجازه را در تلفنتان اعطا کنید."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"برای تماس یا پیامک اضطراری"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"مکان به خدمات اضطراری ارسال شد"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"این برنامه درحین تماس با شماره تلفن اضطراری یا ارسال پیامک به آن به مکان دستگاهتان دسترسی پیدا کرده است. حتی زمانی‌که برنامه اجازه مکان ندارد یا مکان دستگاه خاموش است، ممکن است این اتفاق رخ دهد. "<a href="https://support.google.com/android/answer/9319337">"بیشتر بدانید"</a></string>
</resources>
diff --git a/PermissionController/res/values-fi-v34/strings.xml b/PermissionController/res/values-fi-v34/strings.xml
index b2f95dc19..b00393067 100644
--- a/PermissionController/res/values-fi-v34/strings.xml
+++ b/PermissionController/res/values-fi-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Päätä sovellusten pääsystä terveysdataan"</string>
<string name="location_settings" msgid="8863940440881290182">"Pääsy sijaintiin"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Sovellukset ja palvelut. Vaikka asetus olisi poissa päältä, mikrofonidataa saatetaan silti jakaa, kun soitat hätänumeroon"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Sovellukset ja palvelut"</string>
</resources>
diff --git a/PermissionController/res/values-fi-watch/strings.xml b/PermissionController/res/values-fi-watch/strings.xml
index 843f06a71..296eedf2b 100644
--- a/PermissionController/res/values-fi-watch/strings.xml
+++ b/PermissionController/res/values-fi-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ei muutettavissa"</string>
<string name="generic_yes" msgid="2489207724988649846">"Kyllä"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Peru"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Aina"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Sovelluksen käytön aikana"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Aina"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Sovelluksen käytön aikana"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Aina"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Sovelluksen käytön aikana"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Aina"</string>
</resources>
diff --git a/PermissionController/res/values-fi/strings.xml b/PermissionController/res/values-fi/strings.xml
index 519ce8bc3..34195ba3c 100644
--- a/PermissionController/res/values-fi/strings.xml
+++ b/PermissionController/res/values-fi/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"käyttöoikeudet"</string>
<string name="cancel" msgid="8943320028373963831">"Peru"</string>
<string name="back" msgid="6249950659061523680">"Takaisin"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Sulje"</string>
<string name="available" msgid="6007778121920339498">"Käytettävissä"</string>
<string name="blocked" msgid="9195547604866033708">"Estetty"</string>
<string name="on" msgid="280241003226755921">"Päällä"</string>
@@ -34,6 +35,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 +62,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 +74,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Kaikki luvat"</string>
<string name="other_permissions" msgid="2901186127193849594">"Muut sovellusluvat"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Lupapyyntö"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear ei tue asennus- ja poistotoimintoja."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Valitse, mitä käyttöoikeuksia sovellukselle &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; myönnetään."</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on päivitetty. Valitse, mitä käyttöoikeuksia tälle sovellukselle myönnetään."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Peru"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Salli aina kaikki"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Kysy aina"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Älä salli"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Salli rajoitettu pääsy"</string>
<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_header_with_device_name" msgid="7193042925656173271">"Tällä sovelluksella on pääsy (<xliff:g id="PERM">%1$s</xliff:g>) laitteella <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Poista luvat, jos sovellusta ei käytetä"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Poista lupia ja vapauta tilaa"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Keskeytä sovellustoim. jos ei käytössä"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Hallinnoi sovellusta, jos käyttämätön"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Poista käyttämättömät luvat ja väliaikaiset tiedostot ja pysäytä ilmoitukset"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Poista käyttämättömät luvat ja väliaikaiset tiedostot, pysäytä ilmoitukset ja arkistoi sovellus"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Datasi suojaamiseksi tämän sovelluksen luvat poistetaan, jos sovellusta ei käytetä muutamaan kuukauteen."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Jos sovellusta ei käytetä muutamaan kuukauteen, seuraavat luvat poistetaan datasi suojaamiseksi: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Datasi suojaamiseksi luvat on poistettu sovelluksilta, joita et ole käyttänyt muutamaan kuukauteen."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Kaikkien tiedostojen ylläpito sallittu"</string>
<string name="ask_header" msgid="2633816846459944376">"Kysy aina"</string>
<string name="denied_header" msgid="903209608358177654">"Ei sallittu"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Muut sovellukset, joilla on pääsy kaikkiin tiedostoihin"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 päivä}other{# päivää}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# tunti}other{# tuntia}}"</string>
@@ -399,8 +405,13 @@
<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="role_wallet_label" msgid="3719419175656204207">"Oletuslompakkosovellus"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Lompakkosovellus"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Lompakkosovelluksista on apua erilaisissa tilanteissa, koska niihin voi tallentaa esimerkiksi credit- ja kanta-asiakaskortit ja autonavaimet"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Valitaanko <xliff:g id="APP_NAME">%1$s</xliff:g> oletuslompakkosovelluksesi?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Lupia ei tarvita"</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>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Aseta oletukseksi"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Lisää oletuksia"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Linkkien avaaminen"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Työkäytön oletus"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Oletus yksityiselle tilalle"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ei mitään"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Järjestelmän oletusarvo)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Ei sovelluksia"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Näytä avustajan käynnistyskomennon havaitseminen"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Näytä tilarivillä kuvake, kun ääniapuri aktivoidaan mikrofonin avulla."</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; käyttää laitteellasi olevia kuvia ja mediaa?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn kuviin ja mediaan (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; yhteystietojesi käyttöoikeuden?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn yhteystietoihin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn tämän laitteen sijaintiin?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn sijaintiin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;)?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Sovellus saa sijainnin käyttöoikeuden vain silloin, kun käytät sovellusta"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn tämän laitteen sijaintiin?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn sijaintiin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;)?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Sovellus voi haluta nähdä sijaintisi aina, myös silloin kun et käytä sitä. "<annotation id="link">"Myönnä lupa asetuksista"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Muutetaanko sijainnin käyttöoikeutta (&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;)?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Muutetaanko sijainnin pääsyoikeuksia (&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;) laitteella (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Sovellus haluaa nähdä sijaintisi aina, myös silloin kun et käytä sitä. "<annotation id="link">"Myönnä lupa asetuksista"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; löytää lähellä olevia laitteita, yhdistää niihin ja määrittää niiden suhteellisen sijainnin?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; löytää laitteita lähellä (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;), yhdistää ja määrittää suhteellisen sijainnin?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; löytää lähellä olevia laitteita, yhdistää niihin ja määrittää niiden suhteellisen sijainnin? "<annotation id="link">"Myönnä lupa asetuksista."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Muutetaanko sijainnin käyttöoikeus (<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>) likimääräisestä tarkaksi?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Muutetaanko sijainnin pääsyoikeudet (<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>) laitteella (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;) likimääräisistä tarkoiksi?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn tämän laitteen karkeaan sijaintiin?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn likimääräiseen sijaintiin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Tarkka"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Likimääräinen"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn kalenteriisi?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn kalenteriin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lähettää ja lukea tekstiviestejä?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lähettää ja lukea tekstiviestejä (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; käyttää laitteellasi olevia kuvia, mediaa ja tiedostoja?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn kuviin, mediaan ja tiedostoihin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn laitteen &lt;b&gt;kuviin, videoihin, musiikkiin ja audioon&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn &lt;b&gt;kuviin, videoihin, musiikkiin, audioon ja muihin tiedostoihin&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn tällä laitteella oleviin musiikki- ja audiotiedostoihin?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn musiikkiin ja audioon (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn laitteella oleviin kuviin ja mediaan?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn kuviin ja videoihin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn useampiin laitteella oleviin kuviin ja mediaan?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn myös muihin kuviin ja videoihin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tallentaa audiota?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tallentaa audiota (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Sovellus voi tallentaa audiota vain silloin, kun käytät sitä"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tallentaa audiota?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tallentaa audiota (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Sovellus voi haluta tallentaa audiota aina, myös silloin kun et käytä sitä. "<annotation id="link">"Myönnä lupa asetuksista"</annotation>"."</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Muutetaanko mikrofonin käyttöoikeutta (&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;)?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Muutetaanko mikrofonin pääsyoikeuksia (&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;) laitteella (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Sovellus haluaa tallentaa audiota aina, myös silloin kun et käytä sitä. "<annotation id="link">"Myönnä lupa asetuksista"</annotation>"."</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nähdä liikkumistietosi?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn fyysisen aktiivisuuden tietoihin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ottaa kuvia ja nauhoittaa videoita?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ottaa kuvia ja tallentaa videota (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Sovellus voi ottaa kuvia ja videoita vain silloin, kun käytät sitä"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ottaa kuvia ja videoita?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ottaa kuvia ja tallentaa videota (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Sovellus voi haluta ottaa kuvia ja videoita aina, myös silloin kun et käytä sitä. "<annotation id="link">"Myönnä lupa asetuksista"</annotation>"."</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Muutetaanko kameran käyttöoikeutta (&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;)?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Muutetaanko kameran pääsyoikeuksia (&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;) laitteella (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Sovellus haluaa ottaa kuvia ja videoita aina, myös silloin kun et käytä sitä. "<annotation id="link">"Myönnä lupa asetuksista"</annotation>"."</string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; puhelulokien käyttöoikeuden?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn puhelulokeihin (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; soittaa ja hallinnoida puheluita?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; soittaa ja hallinnoida puheluita (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; käyttää anturitietoja elintoiminnoistasi?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn elintoimintojen anturidataan (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Sovellus haluaa pääsyn elintoimintojasi koskevaan anturidataan aina, myös silloin, kun et käytä sovellusta. Voit myöntää luvan "<annotation id="link">"asetuksista"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; käyttää anturidataa elintoiminnoistasi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pääsyn elintoimintojen anturidataan (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Jos haluat myöntää sovellukselle pääsyn kehoanturidataan aina, myös silloin, kun et käytä sovellusta, "<annotation id="link">"siirry asetuksiin."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Haluatko edelleen sallia pääsyn kehoanturidataan, kun &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; on käytössä?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; edelleen pääsyn kehoanturidataan (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;), kun sovellus on käytössä?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lähettää sinulle ilmoituksia?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Saako &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lähettää sinulle ilmoituksia (&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;)?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Automaattisesti myönnetyt käyttöoikeudet"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"Sovelluksella (<xliff:g id="APP_NAME">%1$s</xliff:g>) on pääsy sijaintiin"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organisaatiosi sallii sovelluksen (<xliff:g id="APP_NAME">%1$s</xliff:g>) pääsyn sijaintiin"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"–"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Viimeiset\n24 tuntia"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 viime\npäivää"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> prosenttia"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> on Androidin suojaama. Koska datasi käsitellään tällä laitteella, sovelluksen lupien käyttö ei näy tilapalkissa eikä yksityisyydenhallintapaneelisi."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> on Androidin suojaama. Koska datasi käsitellään tällä laitteella, sovelluksen lupien käyttö ei näy yksityisyydenhallintapaneelissasi."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Laitteen kamera on estetty"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Sovellusten ja palvelujen osalta"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofonidataa saatetaan silti jakaa, kun soitat hätänumeroon."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Muuta"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kameran pääsyoikeus ei ole päällä"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Pääsy mikrofoniin ei ole käytössä"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Sijaintitietojen käyttöoikeus ei ole käytössä"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Infotainment-sovelluksille"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Pakollisille sovelluksille"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Tämä sovellus tarvitaan"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Autosi valmistaja edellyttää tätä sovellusta"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Turvallisuus ja yksityisyys"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Tarkista laite"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ohita"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Datan jaon päivitykset"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Jotkin sovellukset ovat muuttaneet tapaa, jolla ne voivat jakaa sijaintitietoja"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Asetukset"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Avattu <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Avattu eilen klo <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Avattu <xliff:g id="TIME_DATE_0">%1$s</xliff:g> klo <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Kertakäyttöinen salasanasi on 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Rajoitettu asetus"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Asetus ei ole tällä hetkellä käytettävissä turvallisuussyistä."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Sovellukselta on evätty pääsy: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Sovellus on pyytänyt pääsyä arkaluontoiseen lupaan, joka voi vaarantaa henkilökohtaisia tietojasi ja taloustietojasi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Sovellus ei välttämättä toimi oikein ilman tätä rajoitettua lupaa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lue, miten voit sallia pääsyn&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Sovellus ei saa olla oletuksena <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Sovellus on pyytänyt pääsyä arkaluontoisiin lupiin, jotka voivat vaarantaa henkilökohtaisia tietojasi ja taloustietojasi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Sovellus ei välttämättä toimi oikein ilman näitä rajoitettuja lupia. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lue, miten voit sallia pääsyn&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Sovellukselta on evätty pääsy"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Tämä lupa voi vaarantaa henkilökohtaisia tietojasi ja taloustietojasi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Sovellus ei välttämättä toimi oikein ilman tätä rajoitettua lupaa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Lue, miten voit sallia pääsyn&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Lue lisää"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Lupapyyntö estetty"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Sovellus pyytää lisälupia, mutta lupia ei voi myöntää striimatessa. Myönnä lupa ensin puhelimella."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Soitto tai tekstiviesti hätäkeskukselle"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Sijainti lähetetty hätäkeskukselle"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Tämä sovellus sai pääsyn laitteen sijaintiin, kun soitit tai lähetit tekstiviestin hätänumeroon. Tämä voi tapahtua, vaikka sovelluksella ei olisi sijaintilupaa tai laitteen sijainti olisi pois päältä. "<a href="https://support.google.com/android/answer/9319337">"Lue lisää"</a></string>
</resources>
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..227e9513a 100644
--- a/PermissionController/res/values-fr-rCA-v34/strings.xml
+++ b/PermissionController/res/values-fr-rCA-v34/strings.xml
@@ -20,8 +20,7 @@
<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>
</resources>
diff --git a/PermissionController/res/values-fr-rCA-watch/strings.xml b/PermissionController/res/values-fr-rCA-watch/strings.xml
index 8e92df18e..aa512a724 100644
--- a/PermissionController/res/values-fr-rCA-watch/strings.xml
+++ b/PermissionController/res/values-fr-rCA-watch/strings.xml
@@ -18,8 +18,15 @@
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>
+ <string name="permission_access_always" msgid="2107115233573823032">"En tout temps"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Quand l\'appli est utilisée"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"En tout temps"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Quand l\'appli est utilisée"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"En tout temps"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Quand l\'appli est utilisée"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"En tout temps"</string>
</resources>
diff --git a/PermissionController/res/values-fr-rCA/strings.xml b/PermissionController/res/values-fr-rCA/strings.xml
index 8ad73be8d..d21c55779 100644
--- a/PermissionController/res/values-fr-rCA/strings.xml
+++ b/PermissionController/res/values-fr-rCA/strings.xml
@@ -21,12 +21,13 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"autorisations"</string>
<string name="cancel" msgid="8943320028373963831">"Annuler"</string>
<string name="back" msgid="6249950659061523680">"Retour"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Fermer"</string>
<string name="available" msgid="6007778121920339498">"Accessible"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqué"</string>
<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 +35,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 +44,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 +52,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,32 +72,32 @@
<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>
@@ -108,21 +111,19 @@
<!-- 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 uniquement lorsque 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,12 +131,12 @@
<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>
@@ -163,7 +164,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 +188,33 @@
<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\'application est utilisée"</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="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Autoriser un accès limité"</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_header_with_device_name" msgid="7193042925656173271">"Accès <xliff:g id="PERM">%1$s</xliff:g> pour cette appli sur <xliff:g id="DEVICE_NAME">%2$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_label_v3" msgid="693340578642156657">"Gérer l\'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="unused_apps_summary_v2" msgid="5011313200815115802">"Retirer les autorisations, supprimer les fichiers temporaires, arrêter les notifications et archiver l\'appli"</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 +223,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>
@@ -252,66 +257,67 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Autorisées à gérer tous les fichiers"</string>
<string name="ask_header" msgid="2633816846459944376">"Toujours demander"</string>
<string name="denied_header" msgid="903209608358177654">"Non autorisées"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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="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="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>
@@ -337,71 +343,76 @@
<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="role_wallet_label" msgid="3719419175656204207">"Appli portefeuille par défaut"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Appli de portefeuille"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Les applis de portefeuille peuvent stocker vos cartes de crédit et de fidélité, vos clés de voiture et d\'autres données pour faciliter différentes modalités de transaction."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de portefeuille par défaut?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Aucune autorisation nécessaire"</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,93 +429,121 @@
<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_apps_for_private_profile" msgid="2022024112144880785">"Applis par défaut pour l\'Espace privé"</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_device_aware_storage_isolated" msgid="6463062962458809752">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et aux contenus multimédias sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_contacts" msgid="731025863972535928">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos contacts sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Modifier l\'accès à la position pour &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à détecter les appareils à proximité, à s\'y connecter et à déterminer leur position relative sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"Modifier l\'accès à la position de &lt;b&gt;<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>&lt;/b&gt; sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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_device_aware_coarselocation" msgid="8367540370912066757">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position approximative de &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exacte"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximative"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre agenda?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre agenda sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à envoyer et à afficher des messages texte?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à envoyer et à afficher des messages texte sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos, au contenu multimédia et aux fichiers de votre appareil?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos, aux contenus multimédias et aux fichiers sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder &lt;b&gt;aux photos, aux vidéos, et aux fichiers musicaux et audio&lt;/b&gt; sur cet appareil?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder &lt;b&gt;aux photos, aux vidéos, et aux fichiers musicaux, audio et autres&lt;/b&gt; sur cet appareil?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux fichiers musicaux et audio sur cet appareil?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux fichiers musicaux et audio sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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_device_aware_read_media_visual" msgid="3122576538319059333">"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 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_more_photos" msgid="1703469013613723053">"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 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer de l\'audio sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer de l\'audio sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Modifier l\'accès au microphone pour &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_activityRecognition" msgid="1243869530588745374">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre activité physique sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à prendre des photos et à enregistrer des vidéos sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à prendre des photos et à enregistrer des vidéos sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Modifier l\'accès à l\'appareil photo pour &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_calllog" msgid="8220927190376843309">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos journaux d\'appels téléphoniques sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à passer des appels et à les gérer sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"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 sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"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 sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"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 sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à vous envoyer des notifications sur &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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> dispose d\'un accès à la position"</string>
<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,14 +551,22 @@
<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="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> pour cent"</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="automotive_blocked_camera_title" msgid="6142362431548829416">"L\'accès à l\'appareil photo est désactivé"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"L\'accès au microphone est désactivé"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"L\'accès à la position est désactivé"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Pour les applis d\'infodivertissement"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Pour les applis requises"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Cette appli est requise"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Cette appli est requise par le fabricant de votre voiture"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sécurité et confidentialité"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analyser l\'appareil"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Fermer"</string>
@@ -540,8 +587,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 +609,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 +653,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Mises à jour des pratiques de partage des données"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Certaines applis ont modifié comment elles peuvent partager vos données de localisation"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Paramètres"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Dernier accès : <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Accès hier (<xliff:g id="TIME_DATE">%1$s</xliff:g>)"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Accès : <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Votre mot de passe à usage unique est le suivant : 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Paramètre restreint"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pour protéger votre sécurité, ce paramètre n\'est pas accessible actuellement."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'appli n\'a pas obtenu l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'appli a demandé l\'accès à une autorisation sensible qui pose un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Il est possible que l\'appli ne fonctionne pas correctement sans cette autorisation limitée. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrir comment autoriser l\'accès&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"L\'appli n\'a pas obtenu l\'accès pour avoir le rôle<xliff:g id="ROLE_NAME">%1$s</xliff:g> par défaut"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"L\'appli a demandé l\'accès à des autorisations sensibles qui posent un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Il est possible que l\'appli ne fonctionne pas correctement sans ces autorisations limitées. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrir comment autoriser l\'accès&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"L\'appli n\'a pas obtenu l\'accès"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accès à cette autorisation pose un risque pour vos renseignements personnels et financiers.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Il est possible que l\'appli ne fonctionne pas correctement sans cette autorisation limitée. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrir comment autoriser l\'accès&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"En savoir plus"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Demande d\'autorisation supprimée"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Cette appli demande des autorisations supplémentaires, mais celles-ci ne peuvent pas être accordées lors d\'une session de diffusion en continu. Accordez d\'abord l\'autorisation de votre téléphone."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pour un appel ou un message texte d\'urgence"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Position envoyée aux services d\'urgence"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Cette appli a accédé à la position de votre appareil lors d\'un appel ou de l\'envoi d\'un message texte à un numéro d\'urgence. Cela peut se produire même si l\'appli ne dispose pas de l\'autorisation de localisation ou si la localisation de l\'appareil est désactivée. "<a href="https://support.google.com/android/answer/9319337">"En savoir plus"</a></string>
</resources>
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-v34/strings.xml b/PermissionController/res/values-fr-v34/strings.xml
index f7f584b75..bfd957bcb 100644
--- a/PermissionController/res/values-fr-v34/strings.xml
+++ b/PermissionController/res/values-fr-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Gérer l\'accès de l\'appli aux données de santé"</string>
<string name="location_settings" msgid="8863940440881290182">"Accès à la position"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"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>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Pour les applis et services"</string>
</resources>
diff --git a/PermissionController/res/values-fr-watch/strings.xml b/PermissionController/res/values-fr-watch/strings.xml
index c9df92552..2ef84f46c 100644
--- a/PermissionController/res/values-fr-watch/strings.xml
+++ b/PermissionController/res/values-fr-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Non modifiable"</string>
<string name="generic_yes" msgid="2489207724988649846">"Oui"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Annuler"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Tout le temps"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Quand l\'appli est utilisée"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Tout le temps"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Quand l\'appli est utilisée"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Tout le temps"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Lorsque vous utilisez l\'appli"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Tout le temps"</string>
</resources>
diff --git a/PermissionController/res/values-fr/strings.xml b/PermissionController/res/values-fr/strings.xml
index 9e9d2ccf0..4afe32dbc 100644
--- a/PermissionController/res/values-fr/strings.xml
+++ b/PermissionController/res/values-fr/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"autorisations"</string>
<string name="cancel" msgid="8943320028373963831">"Annuler"</string>
<string name="back" msgid="6249950659061523680">"Retour"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Fermer"</string>
<string name="available" msgid="6007778121920339498">"Disponible"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqué"</string>
<string name="on" msgid="280241003226755921">"Activé"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Toutes les autorisations"</string>
<string name="other_permissions" msgid="2901186127193849594">"Autres fonctionnalités de l\'application"</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">"Opérations d\'installation et de désinstallation impossibles sur Android Wear"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Sélectionner les éléments auxquels &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; peut accéder"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"L\'application &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; a été mise à jour. Sélectionnez les éléments auxquels elle peut accéder."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Annuler"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Toujours 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="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Autoriser un accès limité"</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>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Accès à <xliff:g id="PERM">%1$s</xliff:g> pour cette application sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Voir toutes les autorisations pour <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Voir toutes les applis ayant cette autorisation"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Afficher l\'utilisation du micro par l\'Assistant"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Supprimer les autorisations si l\'application n\'est pas utilisée"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Supprimer autorisations et libérer espace"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Suspendre activité appli si inutilisée"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gérer l\'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="unused_apps_summary_v2" msgid="5011313200815115802">"Retirer les autorisations, supprimer les fichiers temporaires, arrêter les notifications et archiver l\'appli"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Si cette application n\'est pas utilisée pendant plusieurs mois, ses autorisations seront supprimées afin de protéger vos données."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Pour protéger vos données, si l\'application n\'est pas utilisée pendant plusieurs 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 plusieurs mois."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Autorisées à gérer tous les fichiers"</string>
<string name="ask_header" msgid="2633816846459944376">"Toujours demander"</string>
<string name="denied_header" msgid="903209608358177654">"Non autorisé"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> sur <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Voir plus d\'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="hours" msgid="7302866489666950038">"{count,plural, =1{# heure}one{# heure}many{# heures}other{# heures}}"</string>
@@ -375,7 +381,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Appli de notes"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Applis vous permettant de prendre des notes sur votre appareil"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notes"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Appli portefeuille par défaut"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Appli portefeuille numérique"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Les applis de portefeuille numérique stockent vos cartes bancaires, cartes de fidélité, clés de voiture et bien d\'autres choses vous permettant d\'effectuer diverses transactions."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Définir <xliff:g id="APP_NAME">%1$s</xliff:g> comme appli de portefeuille numérique par défaut ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Aucune autorisation nécessaire"</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>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Plus d\'applications 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 utilisation pro"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Paramètres par défaut d\'Espace privé"</string>
<string name="default_app_none" msgid="9084592086808194457">"Aucune"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Application système par défaut)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Aucune application"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Afficher la détection de l\'activation de l\'assistant"</string>
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et médias sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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_device_aware_contacts" msgid="731025863972535928">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à vos contacts sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position de l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Changer l\'autorisation d\'accès à la position pour &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Modifier l\'accès à la position de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Cette appli souhaite 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>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à détecter les appareils à proximité, s\'y connecter et déterminer leur position relative ?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à détecter des appareils à proximité, s\'y connecter et déterminer leur position relative sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à détecter les appareils à proximité, s\'y connecter et déterminer leur position relative ? "<annotation id="link">"Autoriser dans les paramètres"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Donner à <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> l\'accès à la position exacte et non plus approximative ?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Modifier l\'accès à la position de <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; d\'approximative à précise ?"</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_device_aware_coarselocation" msgid="8367540370912066757">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la position approximative de l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exacte"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approximative"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre agenda ?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre agenda sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à envoyer et afficher des SMS ?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à envoyer et à consulter des SMS sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos, contenus multimédias et fichiers sur votre appareil ?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos, médias et fichiers sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux &lt;b&gt;photos, vidéos, fichiers musicaux/audio&lt;/b&gt; sur l\'appareil ?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux &lt;b&gt;photos, vidéos, fichiers musicaux/audio, etc.&lt;/b&gt; sur l\'appareil ?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la musique et à l\'audio sur cet appareil ?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à la musique et à l\'audio sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<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 vidéos sur cet appareil ?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux photos et vidéos sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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 à d\'autres photos et vidéos sur cet appareil ?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à plus de photos et vidéos sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer de l\'audio ?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer du contenu audio sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Cette application ne pourra réaliser des enregistrements audio que lorsque vous l\'utiliserez"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permettre à &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; de réaliser des enregistrements audio ?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à enregistrer du contenu audio sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Cette application peut souhaiter réaliser des enregistrements audio à tout moment, même quand vous ne l\'utilisez pas. "<annotation id="link">"Autoriser dans les paramètres"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Changer l\'autorisation d\'accès au micro pour &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Modifier l\'accès au micro de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Cette application souhaite réaliser des enregistrements audio à tout moment, même quand vous ne l\'utilisez pas. "<annotation id="link">"Autoriser dans les paramètres"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder aux données relatives à votre activité physique ?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder à votre activité physique sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à prendre des photos et à enregistrer des vidéos sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Cette application ne pourra prendre des photos et enregistrer des vidéos que lorsque vous l\'utiliserez"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à prendre des photos et à enregistrer des vidéos sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Cette application peut souhaiter prendre des photos et des vidéos à tout moment, même quand vous ne l\'utilisez pas. "<annotation id="link">"Autoriser dans les paramètres"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Changer l\'autorisation d\'accès à l\'appareil photo pour &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Modifier l\'accès à l\'appareil photo de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Cette application souhaite prendre des photos et des vidéos à tout moment, même quand vous ne l\'utilisez pas. "<annotation id="link">"Autoriser 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 aux journaux d\'appels de votre téléphone ?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux journaux d\'appels de votre téléphone sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à passer et gérer des appels téléphoniques ?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à passer et à gérer des appels téléphoniques sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux données des capteurs sur vos signes vitaux sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</string>
<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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à accéder aux données des capteurs sur vos signes vitaux sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"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 sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Autoriser &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; à vous envoyer des notifications sur l\'appareil &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ?"</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>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Votre organisation autorise <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder à votre position"</string>
@@ -505,13 +544,14 @@
<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>
<string name="privdash_label_none" msgid="5991866260360484858">"Aucune"</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="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> : <xliff:g id="PERCENT">%2$d</xliff:g> pour cent"</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 des autorisations de cette appli n\'est pas affichée sur la barre d\'état, ni sur votre tableau de bord 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 des autorisations de cette appli n\'est pas affichée sur votre tableau de bord Confidentialité."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"La caméra de l\'appareil est bloquée"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Pour les applis et services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Il est cependant possible que les données du micro soient partagées lorsque vous appelez un numéro d\'urgence."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Modifier"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"L\'accès à l\'appareil photo est désactivé"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"L\'accès au micro est désactivé"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"L\'accès à la position est désactivé"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Pour les applis d\'infoloisirs"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Pour les applis requises"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Cette appli est requise"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Cette appli est requise par votre constructeur automobile"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sécurité et confidentialité"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analyser l\'appareil"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ignorer"</string>
@@ -535,7 +582,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 +624,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>
@@ -610,7 +657,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Modifications du partage des données"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Certaines applis ont modifié la façon dont elles peuvent partager vos données de localisation"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Paramètres"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Dernière consultation : <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Dernière consultation : hier, à <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Dernière consultation : <xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Votre mot de passe à usage unique est le suivant : 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Paramètre restreint"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pour votre sécurité, ce paramètre est actuellement indisponible."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"L\'appli s\'est vu refuser l\'accès à <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Cette appli a demandé l\'accès à une autorisation sensible susceptible d\'exposer vos informations financières et personnelles à un risque.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Si elle ne dispose pas d\'un accès à cette autorisation restreinte, l\'appli peut ne pas fonctionner correctement. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrez comment autoriser l\'accès&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"L\'appli s\'est vu refuser l\'accès au statut de <xliff:g id="ROLE_NAME">%1$s</xliff:g> par défaut"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Cette appli a demandé l\'accès à des autorisations sensibles susceptibles d\'exposer vos informations financières et personnelles à un risque.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Si elle ne dispose pas d\'un accès à ces autorisations restreintes, l\'appli peut ne pas fonctionner correctement. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrez comment autoriser l\'accès&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"L\'appli s\'est vu refuser l\'accès"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accès à cette autorisation peut exposer vos informations financières et personnelles à un risque.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Si elle ne dispose pas d\'un accès à cette autorisation restreinte, l\'appli peut ne pas fonctionner correctement. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Découvrez comment autoriser l\'accès&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"En savoir plus"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Demande d\'autorisation supprimée"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Cette appli demande des autorisations supplémentaires, mais il est impossible d\'accorder des autorisations lors d\'une session de streaming. Accordez tout d\'abord l\'autorisation sur votre téléphone."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pour les messages ou les appels d\'urgence"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Position envoyée aux services d\'urgence"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Cette application a accédé à la position de votre appareil pendant un appel ou l\'envoi d\'un message vers un numéro d\'urgence. Cette action se produit même lorsque l\'application ne dispose pas de l\'autorisation d\'accéder à la position ou lorsque la position de l\'appareil est désactivée. "<a href="https://support.google.com/android/answer/9319337">"En savoir plus"</a></string>
</resources>
diff --git a/PermissionController/res/values-gl-v34/strings.xml b/PermissionController/res/values-gl-v34/strings.xml
index cabf7a43a..745a9a615 100644
--- a/PermissionController/res/values-gl-v34/strings.xml
+++ b/PermissionController/res/values-gl-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Xestiona o acceso das aplicacións aos datos de saúde"</string>
<string name="location_settings" msgid="8863940440881290182">"Acceso á localización"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Para aplicacións e servizos. Aínda que esta opción de configuración se atope desactivada, poderán compartirse datos do micrófono se chamas a un número de emerxencias"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Para aplicacións e servizos"</string>
</resources>
diff --git a/PermissionController/res/values-gl-watch/strings.xml b/PermissionController/res/values-gl-watch/strings.xml
index ddb8916bb..060385455 100644
--- a/PermissionController/res/values-gl-watch/strings.xml
+++ b/PermissionController/res/values-gl-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Cambio imposible"</string>
<string name="generic_yes" msgid="2489207724988649846">"Si"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancelar"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Todo o tempo"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Coa aplicación en uso"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Todo o tempo"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Coa aplicación en uso"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Todo o tempo"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Coa aplicación en uso"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Todo o tempo"</string>
</resources>
diff --git a/PermissionController/res/values-gl/strings.xml b/PermissionController/res/values-gl/strings.xml
index ad7705f59..645a96229 100644
--- a/PermissionController/res/values-gl/strings.xml
+++ b/PermissionController/res/values-gl/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permisos"</string>
<string name="cancel" msgid="8943320028373963831">"Cancelar"</string>
<string name="back" msgid="6249950659061523680">"Atrás"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Dispoñible"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqueado"</string>
<string name="on" msgid="280241003226755921">"Activado"</string>
@@ -32,8 +34,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 +63,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>
@@ -114,8 +118,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Todos os permisos"</string>
<string name="other_permissions" msgid="2901186127193849594">"Outras funcionalidades da aplicación"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Solicitude de permiso"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"As accións de instalar e desinstalar non son compatibles con Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Seleccionar os permisos de acceso que queres dar á aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Actualizouse a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Selecciona os permisos de acceso que lle queres dar."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancelar"</string>
@@ -191,12 +193,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Permitir todo sempre"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Preguntar sempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Non permitir"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permitir acceso limitado"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Localización precisa"</string>
<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_header_with_device_name" msgid="7193042925656173271">"Esta aplicación ten o seguinte permiso de acceso en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>: <xliff:g id="PERM">%1$s</xliff:g>"</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>
@@ -204,7 +208,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Quitar permisos se non se usa a aplicación"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Quitar permisos e liberar espazo"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pór en pausa actividade de apps sen uso"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Xestionar aplicación se non se usa"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Quita permisos, elimina ficheiros temporais e detén as notificacións"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Quita permisos, elimina os ficheiros temporais, detén as notificacións e arquiva a aplicación"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para protexer os teus datos, quitaranse os permisos desta aplicación se pasas varios meses sen utilizala."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para protexer os teus datos, se a aplicación leva varios meses sen usarse, quitaranse os seguintes permisos: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para protexer os teus datos, quitáronse os permisos das aplicacións que levas varios meses sen usar."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Permiso para xestionar todos os ficheiros"</string>
<string name="ask_header" msgid="2633816846459944376">"Preguntar sempre"</string>
<string name="denied_header" msgid="903209608358177654">"Permiso non concedido"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> en <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ver máis aplicacións con acceso a todos os ficheiros"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 día}other{# días}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}other{# horas}}"</string>
@@ -384,7 +391,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>
@@ -401,6 +408,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplicación de notas"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplicacións que che permiten tomar notas no dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"App de carteira predeterminada"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplicación de carteira"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"As aplicacións de carteira poden almacenar as túas tarxetas de crédito e de fidelidade, as chaves do coche e outros obxectos para axudar en distintos tipos de transaccións."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Queres establecer <xliff:g id="APP_NAME">%1$s</xliff:g> como aplicación de carteira predeterminada?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Non se necesita ningún permiso"</string>
<string name="request_role_current_default" msgid="738722892438247184">"App predeterminada actual"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Non preguntar de novo"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"App predeterminada"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Máis predeterminadas"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Apertura de ligazóns"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predeterminadas para o traballo"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Aplicacións predeterminadas do espazo privado"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ningunha"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Opción predeterminada do sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Non hai ningunha aplicación"</string>
@@ -455,48 +468,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostrar detección do activador do asistente"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostra unha icona na barra de estado cando se utiliza o micrófono para activar o asistente de voz"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás fotos, ao contido multimedia e aos ficheiros do teu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás fotos e ao contido multimedia no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos teus contactos?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos teus contactos no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización do dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"A aplicación só terá acceso á localización mentres a esteas utilizando"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización deste dispositivo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización do dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Queres cambiar o acceso de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; á localización no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; busque dispositivos próximos, se conecte a eles e determine a súa posición relativa no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"Queres cambiar o acceso á localización da aplicación <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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_device_aware_coarselocation" msgid="8367540370912066757">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á localización aproximada do dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ao teu calendario?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ao teu calendario no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envíe e vexa mensaxes SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envíe e lea mensaxes SMS no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a fotos, contido multimedia e ficheiros no teu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás fotos, ao contido multimedia e aos ficheiros no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás &lt;b&gt;fotos, vídeos, música e audio&lt;/b&gt; deste dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás &lt;b&gt;fotos, vídeos, música, audio e outros ficheiros&lt;/b&gt; do dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á música e aos ficheiros de audio deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á música e aos ficheiros de audio no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás fotos e aos vídeos deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda ás fotos e vídeos no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a máis fotos e vídeos deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda a máis fotos e vídeos no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Esta aplicación só poderá gravar audio cando a esteas utilizando"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave audio no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Pode que esta aplicación queira gravar audio todo o tempo, incluso cando non a esteas utilizando. "<annotation id="link">"Permitir en Configuración."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Queres cambiar o acceso da aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ao micrófono?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Queres cambiar o acceso de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ao micrófono no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Esta aplicación quere gravar audio todo o tempo, incluso cando non a esteas utilizando. "<annotation id="link">"Permitir en Configuración."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á túa actividade física?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda á túa actividade física no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; realice fotos e grave vídeos?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Esta aplicación só poderá sacar fotos e gravar vídeos cando a esteas utilizando"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Queres permitir que a aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; saque fotos e grave vídeos?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Pode que esta aplicación queira sacar fotos e gravar vídeos todo o tempo, incluso cando non a esteas utilizando. "<annotation id="link">"Permitir en Configuración."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Queres cambiar o acceso da aplicación &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; á cámara?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Queres cambiar o acceso de &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; á cámara no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Esta aplicación quere sacar fotos e gravar vídeos todo o tempo, incluso cando non a esteas utilizando. "<annotation id="link">"Permitir en Configuración."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos rexistros de chamadas do teléfono?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos rexistros de chamadas telefónicas no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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_device_aware_phone" msgid="590399263670349955">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faga e xestione chamadas telefónicas no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Queres 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 no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos datos dos sensores sobre túas constantes vitais no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Queres seguir permitindo que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acceda aos datos dos sensores corporais no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; mentres se estea usando a aplicación?"</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>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Queres permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; che envíe notificacións no dispositivo &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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> ten acceso á localización"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"A túa organización permite que <xliff:g id="APP_NAME">%1$s</xliff:g> acceda á túa localización"</string>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ningún"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Últimas\n24 horas"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Últimos\n7 días"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>, <xliff:g id="PERCENT">%2$d</xliff:g> por cento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está protexida por Android. Dado que os teus datos se procesan neste dispositivo, o uso de permisos por parte desta aplicación non se mostra na barra de estado nin no teu panel de privacidade."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> está protexida por Android. Dado que os teus datos se procesan neste dispositivo, o uso de permisos por parte desta aplicación non se mostra no teu panel de privacidade."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"A cámara do dispositivo está bloqueada"</string>
@@ -520,6 +561,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para aplicacións e servizos"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Con todo, é posible que se compartan os datos do micrófono cando chames a un número de emerxencias."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Cambiar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"O acceso á cámara está desactivado"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"O acceso ao micrófono está desactivado"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"O acceso á localización está desactivado"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para as aplicacións de información e entretemento"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para as aplicacións requiridas"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Requírese esta aplicación"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"O fabricante do teu vehículo require esta aplicación"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Seguranza e privacidade"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analizar dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Pechar"</string>
@@ -619,4 +667,26 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Actualizacións de uso compartido de datos"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Algunhas aplicacións cambiaron a forma en que poden compartir os teus datos de localización"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Configuración"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Último acceso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acceso: onte, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acceso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"O teu contrasinal dun só uso é 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Opción de configuración restrinxida"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pola túa seguranza, esta opción de configuración non está dispoñible nestes momentos."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Denegóuselle á aplicación o acceso ao permiso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"A aplicación solicitou acceso a un permiso confidencial que pode poñer en perigo a túa información persoal e financeira.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É posible que a aplicación non funcione correctamente sen este permiso restrinxido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Máis información sobre como permitir o acceso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Denegóuselle á aplicación o acceso para actuar como <xliff:g id="ROLE_NAME">%1$s</xliff:g> de forma predeterminada"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"A aplicación solicitou acceso a permisos confidenciais que poden poñer en perigo a túa información persoal e financeira.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É posible que a aplicación non funcione correctamente sen estes permisos restrinxidos. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Máis información sobre como permitir o acceso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Denegóuselle á aplicación o acceso"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"O acceso a este permiso pode poñer en perigo a túa información persoal e financeira.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É posible que a aplicación non funcione correctamente sen este permiso restrinxido. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Máis información sobre como permitir o acceso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Máis información"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Aceptar"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Eliminouse a solicitude de permiso"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta aplicación solicita permisos adicionais, pero non se poden conceder nunha sesión de reprodución en tempo real. Primeiro tes que dar o permiso no teu teléfono."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-gu-v34/strings.xml b/PermissionController/res/values-gu-v34/strings.xml
index ba8a5deb0..cce1bac6a 100644
--- a/PermissionController/res/values-gu-v34/strings.xml
+++ b/PermissionController/res/values-gu-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-gu-watch/strings.xml b/PermissionController/res/values-gu-watch/strings.xml
index 66f9cd41b..12db58045 100644
--- a/PermissionController/res/values-gu-watch/strings.xml
+++ b/PermissionController/res/values-gu-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"બદલી શકતાં નથી"</string>
<string name="generic_yes" msgid="2489207724988649846">"હા"</string>
<string name="generic_cancel" msgid="2631708607129269698">"રદ કરો"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"હંમેશાં"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ઍપનો ઉપયોગ કરતા હો ત્યારે"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"હંમેશાં"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ઍપનો ઉપયોગ કરતા હો ત્યારે"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"હંમેશાં"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ઍપનો ઉપયોગ કરતા હો ત્યારે"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"હંમેશાં"</string>
</resources>
diff --git a/PermissionController/res/values-gu/strings.xml b/PermissionController/res/values-gu/strings.xml
index 1d9126a86..ab3216285 100644
--- a/PermissionController/res/values-gu/strings.xml
+++ b/PermissionController/res/values-gu/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"પરવાનગીઓ"</string>
<string name="cancel" msgid="8943320028373963831">"રદ કરો"</string>
<string name="back" msgid="6249950659061523680">"પાછળ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"બંધ કરો"</string>
<string name="available" msgid="6007778121920339498">"ઉપલબ્ધ છે"</string>
<string name="blocked" msgid="9195547604866033708">"બ્લૉક કરેલો છે"</string>
<string name="on" msgid="280241003226755921">"ચાલુ છે"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear પર ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ ક્રિયાઓ સમર્થિત નથી."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને શેના ઍક્સેસ માટેની મંજૂરી આપવી તે પસંદ કરો"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; અપડેટ કરવામાં આવી છે. આ ઍપને શેના ઍક્સેસ માટેની મંજૂરી આપવી તે પસંદ કરો."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"રદ કરો"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"મર્યાદિત ઍક્સેસની મંજૂરી આપો"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> પર આ ઍપ માટે <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"જો ઍપ વાપરતા ન હો, તો તેને મેનેજ કરો"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"પરવાનગીઓ કાઢી નાખો, હંગામી ફાઇલો ડિલીટ કરો અને નોટિફિકેશન બંધ કરો"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"પરવાનગીઓ કાઢી નાખો, હંગામી ફાઇલો ડિલીટ કરો, નોટિફિકેશન બંધ કરો અને ઍપને આર્કાઇવ કરો"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"તમારા ડેટાની સુરક્ષા કરવા માટે, જો ઍપનો કેટલાક મહિનાથી ઉપયોગ કરવામાં આવ્યો ન હોય, તો આ ઍપની પરવાનગીઓ કાઢી નાખવામાં આવશે."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"તમારા ડેટાની સુરક્ષા કરવા માટે, જો ઍપનો કેટલાક મહિનાથી ઉપયોગ કરવામાં આવ્યો ન હોય, તો નીચેની પરવાનગીઓ કાઢી નાખવામાં આવશે: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"તમારા ડેટાની સુરક્ષા માટે, તમારા દ્વારા કેટલાક મહિનાથી ઉપયોગમાં ન લેવાયેલી ઍપની પરવાનગીઓ કાઢી નાખવામાં આવી છે."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"બધી ફાઇલો મેનેજ કરવાની મંજૂરી છે"</string>
<string name="ask_header" msgid="2633816846459944376">"દર વખતે પૂછો"</string>
<string name="denied_header" msgid="903209608358177654">"મંજૂરી નથી"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> પર <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"બધી ફાઇલોને ઍક્સેસ કરી શકે તેવી વધુ ઍપ જુઓ"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 દિવસ}one{# દિવસ}other{# દિવસ}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# કલાક}one{# કલાક}other{# કલાક}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ડિફૉલ્ટ વૉલેટ ઍપ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet ઍપ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"વૉલેટ સંબંધી ઍપ તમારા ક્રેડિટ અને લૉયલ્ટિ કાર્ડ, કાર કી તેમજ વિવિધ વ્યવહારોમાં સહાય કરી શકતી અન્ય બાબતો સ્ટોર કરી શકે છે."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"શું <xliff:g id="APP_NAME">%1$s</xliff:g>ને તમારી ડિફૉલ્ટ વૉલેટ ઍપ તરીકે સેટ કરીએ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"કોઈ પરવાનગી જરૂરી નથી"</string>
<string name="request_role_current_default" msgid="738722892438247184">"હાલની ડિફૉલ્ટ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ફરીથી પૂછશો નહીં"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ડિફૉલ્ટ તરીકે સેટ"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"વધુ ડિફૉલ્ટ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"લિંક ખોલવી"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"કાર્ય માટે ડિફૉલ્ટ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ખાનગી સ્પેસ માટે ડિફૉલ્ટ"</string>
<string name="default_app_none" msgid="9084592086808194457">"કોઈ નહીં"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(સિસ્ટમ ડિફૉલ્ટ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"કોઈ ઍપ નથી"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; પર &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; માટે લોકેશનનો ઍક્સેસ બદલીએ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"તમે આ ઍપનો ઉપયોગ કરી રહ્યાં ન હો, તો પણ તે હંમેશાં તમારા સ્થાનને ઍક્સેસ કરવા માગે છે. "<annotation id="link">"સેટિંગમાંથી મંજૂરી આપો."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને નજીકના ડિવાઇસને શોધવાની, તેને કનેક્ટ કરવાની તેમજ સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપીએ?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>નો &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; પર &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; માટે માઇક્રોફોન ઍક્સેસ બદલીએ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; પર &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; માટે કૅમેરાનો ઍક્સેસ બદલીએ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; પર તમારા આરોગ્ય વિશેની મહત્ત્વપૂર્ણ માહિતી આપતા સંકેતો વિશેનો સેન્સરનો ડેટા ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"જ્યારે તમે ઍપનો ઉપયોગ કરી રહ્યાં ન હો, ત્યારે પણ આ ઍપને બૉડી સેન્સરનો ડેટા ઍક્સેસ કરવાની મંજૂરી આપવા માટે, "<annotation id="link">"સેટિંગ પર જાઓ."</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_device_aware_sensors" msgid="5542771499929819675">"ઍપ ઉપયોગમાં હોય ત્યારે &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ને &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"એકપણ નહીં"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"છેલ્લા\n24 કલાક"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"છેલ્લા\n7 દિવસ"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ટકા"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g>ને Android વડે સુરક્ષિત કરવામાં આવી છે. આ ડિવાઇસમાં તમારા ડેટા પર પ્રક્રિયા કરવામાં આવતી હોવાથી, આ ઍપની પરવાનગીના વપરાશની માહિતી સ્ટેટસ બાર અથવા તમારા પ્રાઇવસી ડૅશબોર્ડ પર બતાવવામાં આવતી નથી."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"કૅમેરાનો ઍક્સેસ બંધ છે"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"માઇક્રોફોનનો ઍક્સેસ બંધ છે"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"લોકેશનનો ઍક્સેસ બંધ છે"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ઇન્ફોટેનમેન્ટ ઍપ માટે"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"જરૂરી ઍપ માટે"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"આ ઍપ જરૂરી છે"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"આ ઍપ તમારી કારના નિર્માતા માટે જરૂરી છે"</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>
@@ -581,7 +628,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 +655,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"ઍક્સેસ કર્યાનો સમય <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ઍક્સેસ કર્યાનો સમય ગઈકાલે <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"ઍક્સેસ કર્યાનો સમય <xliff:g id="TIME_DATE_0">%1$s</xliff:g>ના રોજ <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"તમારો એક-વખત વપરાશનો પાસવર્ડ 132435 છે"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"પ્રતિબંધિત સેટિંગ"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"તમારી સુરક્ષા માટે, આ સેટિંગ હાલમાં ઉપલબ્ધ નથી."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ઍપને <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>નો ઍક્સેસ નકારવામાં આવ્યો"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"આ ઍપ દ્વારા કોઈ સંવેદનશીલ માહિતીની પરવાનગીના ઍક્સેસની વિનંતી કરવામાં આવી છે, જેને કારણે તમારી વ્યક્તિગત અને નાણાકીય માહિતી જોખમમાં આવી શકે છે.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>એ પણ શક્ય છે કે આ પ્રતિબંધિત પરવાનગી વિના ઍપ કદાચ યોગ્ય રીતે કામ ન પણ કરી શકે. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ઍક્સેસ મંજૂર કરવાની રીત જાણો&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ઍપને ડિફૉલ્ટ <xliff:g id="ROLE_NAME">%1$s</xliff:g> બનવાનો ઍક્સેસ નકારવામાં આવ્યો"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"આ ઍપ દ્વારા કોઈ સંવેદનશીલ માહિતીની પરવાનગીઓના ઍક્સેસની વિનંતી કરવામાં આવી છે, જેને કારણે તમારી વ્યક્તિગત અને નાણાકીય માહિતી જોખમમાં આવી શકે છે.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>એ પણ શક્ય છે કે આ પ્રતિબંધિત પરવાનગી વિના ઍપ કદાચ યોગ્ય રીતે કામ ન પણ કરી શકે. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ઍક્સેસ મંજૂર કરવાની રીત જાણો&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ઍપને ઍક્સેસ નકારવામાં આવ્યો"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"આ પરવાનગીના ઍક્સેસને કારણે તમારી વ્યક્તિગત અને નાણાકીય માહિતી જોખમમાં આવી શકે છે.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>એ પણ શક્ય છે કે આ પ્રતિબંધિત પરવાનગી વિના ઍપ કદાચ યોગ્ય રીતે કામ ન પણ કરી શકે. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ઍક્સેસ મંજૂર કરવાની રીત જાણો&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"વધુ જાણો"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ઓકે"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"પરવાનગીની વિનંતી નકારવામાં આવી"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"આ ઍપ વધારાની પરવાનગીઓની વિનંતી કરી રહી છે પણ સ્ટ્રીમિંગ સત્રમાં પરવાનગીઓને મંજૂરી આપી શકાતી નથી. પહેલા તમારા ફોન પર પરવાનગીની મંજૂરી આપો."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ઇમર્જન્સી કૉલ કે ટેક્સ્ટ માટે"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ઇમર્જન્સી સર્વિસને મોકલવામાં આવેલું લોકેશન"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"કોઈ ઇમર્જન્સી નંબરને કૉલ કે ટેક્સ્ટ કરવા દરમિયાન આ ઍપ દ્વારા તમારા ડિવાઇસના લોકેશનને ઍક્સેસ કરવામાં આવ્યું. ઍપને લોકેશનની પરવાનગી ન હોય કે ડિવાઇસનું લોકેશન સેટિંગ બંધ કરેલું હોય, ત્યારે પણ આમ થઈ શકે છે. "<a href="https://support.google.com/android/answer/9319337">"વધુ જાણો"</a></string>
</resources>
diff --git a/PermissionController/res/values-hi-v34/strings.xml b/PermissionController/res/values-hi-v34/strings.xml
index 7b95fb793..52826b14c 100644
--- a/PermissionController/res/values-hi-v34/strings.xml
+++ b/PermissionController/res/values-hi-v34/strings.xml
@@ -20,8 +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>
</resources>
diff --git a/PermissionController/res/values-hi-watch/strings.xml b/PermissionController/res/values-hi-watch/strings.xml
index d8ddc7878..12d4356d3 100644
--- a/PermissionController/res/values-hi-watch/strings.xml
+++ b/PermissionController/res/values-hi-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"बदला नहीं जा सकता"</string>
<string name="generic_yes" msgid="2489207724988649846">"हां"</string>
<string name="generic_cancel" msgid="2631708607129269698">"रद्द करें"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"हर समय"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ऐप इस्तेमाल करते समय"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"हर समय"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ऐप इस्तेमाल करते समय"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"हर समय"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ऐप इस्तेमाल करते समय"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"हर समय"</string>
</resources>
diff --git a/PermissionController/res/values-hi/strings.xml b/PermissionController/res/values-hi/strings.xml
index bad54b39a..e0cf4eac9 100644
--- a/PermissionController/res/values-hi/strings.xml
+++ b/PermissionController/res/values-hi/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"अनुमतियां"</string>
<string name="cancel" msgid="8943320028373963831">"रद्द करें"</string>
<string name="back" msgid="6249950659061523680">"वापस जाएं"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"बंद करें"</string>
<string name="available" msgid="6007778121920339498">"उपलब्ध है"</string>
<string name="blocked" msgid="9195547604866033708">"ब्लॉक किया गया"</string>
<string name="on" msgid="280241003226755921">"चालू है"</string>
@@ -34,6 +35,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 +51,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear पर ऐप्लिकेशन इंस्टॉल या अनइंस्टॉल नहीं किए जा सकते."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"चुनें कि &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को किन चीज़ों को ऐक्सेस करने की अनुमति दी जाए"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को अपडेट कर दिया गया है. चुनें कि इस ऐप्लिकेशन को किन चीज़ों को ऐक्सेस करने की अनुमति दी जाए."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"रद्द करें"</string>
@@ -191,20 +192,24 @@
<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_limited_access" msgid="8824410215149764113">"सीमित ऐक्सेस देने की अनुमति दें"</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_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_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> पर इस ऐप्लिकेशन के लिए <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>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"इस्तेमाल नहीं हुआ ऐप्लिकेशन मैनेज करें"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ऐप्लिकेशन की अनुमतियां हटाएं, डिवाइस में कुछ समय के लिए रहने वाली फ़ाइलें मिटाएं, और सूचनाएं रोकें"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ऐप्लिकेशन की अनुमतियां हटाएं, डिवाइस में कुछ समय के लिए रहने वाली फ़ाइलें मिटाएं, सूचनाएं रोकें, और ऐप्लिकेशन संग्रहित करें"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"अगर इस ऐप्लिकेशन का इस्तेमाल कुछ महीनों तक नहीं किया गया, तो इसे दी गई अनुमतियां हटा दी जाएंगी. ऐसा आपके डेटा को सुरक्षित रखने के लिए किया जाएगा."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"अगर ऐप्लिकेशन कुछ महीनों से इस्तेमाल नहीं हुआ है, तो आपके डेटा को सुरक्षित रखने के लिए ये अनुमतियां हटा दी जाएंगी: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"जिन ऐप्लिकेशन का इस्तेमाल कुछ महीनों से नहीं हुआ है उन्हें दी गई अनुमतियां हटा दी गई हैं. ऐसा आपके डेटा को सुरक्षित रखने के लिए किया गया है."</string>
@@ -218,8 +223,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ये ऐप्लिकेशन सभी फ़ाइलों को मैनेज कर सकते हैं"</string>
<string name="ask_header" msgid="2633816846459944376">"हर बार पूछें"</string>
<string name="denied_header" msgid="903209608358177654">"अनुमति नहीं है"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> पर <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ऐसे और ऐप्लिकेशन देखें जो सभी फ़ाइलों को ऐक्सेस कर सकते हैं"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 दिन}one{# दिन}other{# दिन}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# घंटा}one{# घंटा}other{# घंटे}}"</string>
@@ -349,21 +355,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 +381,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"डिफ़ॉल्ट वॉलेट ऐप्लिकेशन"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"वॉलेट ऐप्लिकेशन"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"वॉलेट ऐप्लिकेशन में, अलग-अलग तरह के लेन-देन में इस्तेमाल होने वाली चीज़ों को स्टोर किया जा सकता है. जैसे, क्रेडिट कार्ड, लॉयल्टी कार्ड, कार की डिजिटल कुंजी वगैरह."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"क्या <xliff:g id="APP_NAME">%1$s</xliff:g> को अपने डिफ़ॉल्ट वॉलेट ऐप्लिकेशन के तौर पर सेट करना है?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"अनुमति लेना ज़रूरी नहीं है"</string>
<string name="request_role_current_default" msgid="738722892438247184">"मौजूदा डिफ़ॉल्ट"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"फिर से न पूछें"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"डिफ़ॉल्ट के रूप में सेट करें"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"और डिफ़ॉल्ट ऐप्लिकेशन देखें"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"खुलने वाले लिंक"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"काम के लिए डिफ़ॉल्ट"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"प्राइवेट स्पेस के लिए डिफ़ॉल्ट ऐप्लिकेशन"</string>
<string name="default_app_none" msgid="9084592086808194457">"कोई नहीं"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डिफ़ॉल्ट)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"कोई ऐप्लिकेशन नहीं"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_location" msgid="1264484517831380016">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; के लिए, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"क्या &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; के लिए, <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_device_aware_coarselocation" msgid="8367540370912066757">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;’s की जगह की अनुमानित जानकारी ऐक्सेस करने की अनुमति देनी है?"</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_device_aware_calendar" msgid="7161929851377463612">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g> में मौजूद संगीत और ऑडियो का ऐक्सेस देना है?"</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_device_aware_read_media_visual" msgid="3122576538319059333">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; के लिए, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को आपके &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; के लिए, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; में मौजूद, शरीर के बारे में जानकारी देने वाले सेंसर डेटा का ऐक्सेस देना है?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"इस ऐप्लिकेशन का इस्तेमाल न किए जाने पर भी, इसे बॉडी सेंसर के डेटा को हमेशा ऐक्सेस करने की अनुमति देने के लिए, "<annotation id="link">"सेटिंग पर जाएं."</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_device_aware_sensors" msgid="5542771499929819675">"क्या इस्तेमाल के दौरान, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; पर &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को बॉडी सेंसर के डेटा का ऐक्सेस देते रहना है?"</string>
+ <string name="permgrouprequest_notifications" msgid="6396739062335106181">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को सूचनाएं भेजने की अनुमति देनी है?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"क्या &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; को &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,14 +551,22 @@
<string name="privdash_label_none" msgid="5991866260360484858">"कोई नहीं"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"पिछले\n24 घंटे"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"पिछले\nसात दिन"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> प्रतिशत"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> को Android की मदद से सुरक्षित किया गया है. आपका डेटा इस डिवाइस पर प्रोसेस किया गया है. इसलिए, इस ऐप्लिकेशन की अनुमति के इस्तेमाल की जानकारी, स्टेटस बार या प्राइवसी डैशबोर्ड पर नहीं दिखती."</string>
<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>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"कैमरे का ऐक्सेस बंद है"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"माइक्रोफ़ोन इस्तेमाल करने की अनुमति नहीं है"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"जगह की जानकारी इस्तेमाल करने की अनुमति नहीं है"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"सूचना और मनोरंजन की सुविधा देने वाले ऐप्लिकेशन के लिए"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ज़रूरी ऐप्लिकेशन के लिए"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"यह ऐप्लिकेशन ज़रूरी है"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"आपकी कार के मैन्युफ़ैक्चरर के लिए, यह ऐप्लिकेशन ज़रूरी है"</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>
@@ -608,9 +655,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> को ऐक्सेस किया गया"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"<xliff:g id="TIME_DATE">%1$s</xliff:g> को कल ऐक्सेस किया गया था"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> को ऐक्सेस किया गया"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"एक बार इस्तेमाल होने वाला पासवर्ड 132435 है"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"इस सेटिंग पर पाबंदी लगाई गई है"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"आपकी सुरक्षा के लिए, यह सेटिंग फ़िलहाल उपलब्ध नहीं है."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ऐप्लिकेशन को, <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> को ऐक्सेस करने की अनुमति नहीं मिली"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"इस ऐप्लिकेशन ने संवेदनशील जानकारी ऐक्सेस करने का अनुरोध किया है. इसे अनुमति देने पर, आपकी निजी और वित्तीय जानकारी की सुरक्षा को खतरा हो सकता है.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>हालांकि, हो सकता है कि पाबंदी वाली अनुमति न मिलने पर, ऐप्लिकेशन सही तरह से काम न करें. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ऐक्सेस देने का तरीका जानें&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ऐप्लिकेशन को डिफ़ॉल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> के तौर पर सेट करने की अनुमति नहीं दी गई"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"इस ऐप्लिकेशन ने संवेदनशील जानकारी ऐक्सेस करने का अनुरोध किया है. इसे अनुमति देने पर, आपकी निजी और वित्तीय जानकारी की सुरक्षा को खतरा हो सकता है.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>हालांकि, हो सकता है कि पाबंदी वाली अनुमतियां न मिलने पर, ऐप्लिकेशन सही तरह से काम न करे. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ऐक्सेस देने का तरीका जानें&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ऐप्लिकेशन को ऐक्सेस से जुड़ी अनुमति नहीं दी गई"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ऐप्लिकेशन को यह अनुमति देने पर, आपकी निजी और वित्तीय जानकारी की सुरक्षा को खतरा हो सकता है.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>हालांकि, हो सकता है कि पाबंदी वाली अनुमति न मिलने पर, ऐप्लिकेशन सही तरह से काम न करे. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ऐक्सेस देने का तरीका जानें&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ज़्यादा जानें"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ठीक है"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"अनुमति का अनुरोध अस्वीकार किया गया"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"यह ऐप्लिकेशन ज़्यादा अनुमतियों का अनुरोध कर रहा है. हालांकि, स्ट्रीमिंग सेशन के दौरान अनुमतियां नहीं दी जा सकतीं. सबसे पहले अपने फ़ोन पर अनुमति दें."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"आपातकालीन कॉल या मैसेज करने के लिए"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"आपातकालीन सेवाओं को जगह की जानकारी भेजी गई"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ऐसा हो सकता है कि किसी आपातकालीन नंबर पर कॉल या मैसेज करने के दौरान, ऐप्लिकेशन ने आपके डिवाइस की जगह की जानकारी ऐक्सेस कर ली हो. ऐप्लिकेशन के पास जगह की जानकारी ऐक्सेस करने की अनुमति न होने या डिवाइस की जगह की जानकारी की सेटिंग बंद होने पर भी, ऐप्लिकेशन ऐसा कर सकता है. "<a href="https://support.google.com/android/answer/9319337">"ज़्यादा जानें"</a></string>
</resources>
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-v34/strings.xml b/PermissionController/res/values-hr-v34/strings.xml
index 74a1d7d1e..df017c05d 100644
--- a/PermissionController/res/values-hr-v34/strings.xml
+++ b/PermissionController/res/values-hr-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Upravljajte kojim podacima o zdravlju pristupaju aplikacije"</string>
<string name="location_settings" msgid="8863940440881290182">"Pristup lokaciji"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Za aplikacije i usluge. Ako je ta postavka isključena, podaci mikrofona i dalje se mogu dijeliti kad nazovete broj hitne službe"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Za aplikacije i usluge"</string>
</resources>
diff --git a/PermissionController/res/values-hr-watch/strings.xml b/PermissionController/res/values-hr-watch/strings.xml
index cb04d90e9..3e1f56a9a 100644
--- a/PermissionController/res/values-hr-watch/strings.xml
+++ b/PermissionController/res/values-hr-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Promjena nemoguća"</string>
<string name="generic_yes" msgid="2489207724988649846">"Da"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Otkaži"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Cijelo vrijeme"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Dok se aplikacija koristi"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Cijelo vrijeme"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Dok se aplikacija koristi"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Cijelo vrijeme"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Dok se aplikacija koristi"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Cijelo vrijeme"</string>
</resources>
diff --git a/PermissionController/res/values-hr/strings.xml b/PermissionController/res/values-hr/strings.xml
index b63d820f2..1bc872cfd 100644
--- a/PermissionController/res/values-hr/strings.xml
+++ b/PermissionController/res/values-hr/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"dopuštenja"</string>
<string name="cancel" msgid="8943320028373963831">"Odustani"</string>
<string name="back" msgid="6249950659061523680">"Natrag"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Zatvori"</string>
<string name="available" msgid="6007778121920339498">"Dostupno"</string>
<string name="blocked" msgid="9195547604866033708">"Blokirano"</string>
<string name="on" msgid="280241003226755921">"Uključeno"</string>
@@ -29,11 +30,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Sva dopuštenja"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ostale mogućnosti aplikacije"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Zahtijevanje dopuštenja"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Radnje instaliranja i deinstaliranja nisu podržane na Wearu."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Odaberite čemu će &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; moći pristupiti"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ažurirana je. Odaberite čemu će moći pristupiti."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Otkaži"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Uvijek dopusti sve"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Pitaj svaki put"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nemoj dopustiti"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Omogući ograničeni pristup"</string>
<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_header_with_device_name" msgid="7193042925656173271">"Pristup značajke <xliff:g id="PERM">%1$s</xliff:g> za ovu aplikaciju na uređaju <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Ukloni dopuštenja ako se aplikacija ne upotrebljava"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Ukloni dopuštenja i oslobodi prostor"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pauziraj aktivnosti u aplikacijama ako se ne koriste"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Upravljaj aplikacijom koja se ne koristi"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Uklonite dopuštenja, izbrišite privremene datoteke i zaustavite obavijesti"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Uklanjaju se dopuštenja, brišu privremene datoteke, zaustavljaju obavijesti i aplikacija se arhivira"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Kako bi se vaši podaci zaštitili, dopuštenja za ovu aplikaciju uklonit će se ako se aplikacija ne upotrebljava nekoliko mjeseci."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Kako bi se vaši podaci zaštitili, ako se aplikacija ne upotrebljava nekoliko mjeseci, uklonit će se sljedeća dopuštenja: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Radi zaštite vaših podataka uklonjena su dopuštenja aplikacijama koje nekoliko mjeseci niste upotrebljavali."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Dopušteno upravljanje svim datotekama"</string>
<string name="ask_header" msgid="2633816846459944376">"Pitaj svaki put"</string>
<string name="denied_header" msgid="903209608358177654">"Nemaju dopuštenje"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Pogledajte koje još aplikacije imaju pristup svim datotekama"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}few{# dana}other{# dana}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# sat}one{# sat}few{# sata}other{# sati}}"</string>
@@ -347,9 +353,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 +379,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikacija za bilješke"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikacije koje vam omogućuju vođenje bilješki na uređaju"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"napomene"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Zadana aplikacija za novčanik"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikacija za novčanik"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikacije za novčanik mogu pohranjivati vaše kreditne kartice i kartice vjernosti, automobilske ključeve i druge stvari te vam pomoći s različitim transakcijama."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Želite li aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> postaviti kao zadanu aplikaciju za novčanik?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Dopuštenja nisu potrebna"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Trenutačna zadana"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Više me ne pitaj"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Postavi kao zadano"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Više zadanih aplikacija"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Otvaranje veza"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Zadano za posao"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Zadano za privatni prostor"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nijedna"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Zadana postavka sustava)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nema aplikacija"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Prikaz otkrivanja okidača asistenta"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Prikazuje ikonu na traci statusa kada se za aktiviranje glasovne pomoći upotrebljava mikrofon"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama i medijima na vašem uređaju?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa fotografijama i medijima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da pristupa vašim kontaktima?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa vašim kontaktima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa lokaciji uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacija će imati pristup lokaciji samo dok upotrebljavate aplikaciju"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da pristupa lokaciji ovog uređaja?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa lokaciji uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ova aplikacija možda će uvijek htjeti imati pristup vašoj lokaciji, čak i kad je ne koristite. "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Želite li promijeniti pristup lokaciji za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Želite li promijeniti postavku pristupa lokaciji aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ova aplikacija želi uvijek pristupati vašoj lokaciji, čak i kad je ne koristite. "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da traži uređaje u blizini, povezuje se s njima i određuje njihov približni položaj?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pronađe uređaje u blizini, odredi njihov relativan položaj i poveže se s njima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Dopustiti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; traži uređaje u blizini, poveže se s njima i odredi njihov približni položaj? "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Želite li aplikaciji <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> promijeniti pristup iz približne lokacije u točnu?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Želite li za aplikaciju <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> promijeniti pristup za lokaciju na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g> iz približne u točnu?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Želite li dopustiti 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_device_aware_coarselocation" msgid="8367540370912066757">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa približnoj lokaciji uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Točno"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Približno"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Želite li dopustiti 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_device_aware_calendar" msgid="7161929851377463612">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa vašem kalendaru na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da šalje i pregledava SMS poruke?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da šalje i prikazuje SMS poruke na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Želite li dopustiti aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; da pristupa fotografijama, medijima i datotekama na vašem uređaju?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa fotografijama, medijima i datotekama na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<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_device_aware_read_media_aural" msgid="7927884506238101064">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa glazbi i audiodatotekama na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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_device_aware_read_media_visual" msgid="3122576538319059333">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa fotografijama i videozapisima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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_device_aware_more_photos" msgid="1703469013613723053">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti pristup većem broju fotografija i videozapisa na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da snima zvuk na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija će moći snimati audiozapise samo dok je upotrebljavate"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Ž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="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da snima zvuk na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Aplikacija će možda snimati audiozapise u svakom trenutku, čak i kad je ne upotrebljavate. "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Želite li promijeniti pristup mikrofonu za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Želite li promijeniti postavku pristupa mikrofonu aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Aplikacija traži dopuštenje za snimanje audiozapisa u svakom trenutku, čak i kad je ne upotrebljavate. "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Želite li dopustiti da &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristupa vašoj tjelesnoj aktivnosti?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa vašoj tjelesnoj aktivnosti na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da snima fotografije i videozapise?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da snima slike i videozapise na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija će moći snimati fotografije i videozapise samo dok je upotrebljavate"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da snima fotografije i videozapise?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da snima slike i videozapise na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Aplikacija će možda snimati fotografije i videozapise u svakom trenutku, čak i kad je ne upotrebljavate. "<annotation id="link">"Dopustite u postavkama."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Želite li promijeniti pristup kameri za aplikaciju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Želite li promijeniti postavku pristupa kameri aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g> na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<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_device_aware_calllog" msgid="8220927190376843309">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa zapisnicima poziva vašeg telefona na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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_device_aware_phone" msgid="590399263670349955">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da upućuje telefonske pozive i upravlja njima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa podacima senzora o vašim vitalnim znakovima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da pristupa podacima senzora o vašim vitalnim znakovima na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</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>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Nastaviti dopuštati aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pristup podacima s biometrijskih senzora dok je upotrebljavate?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Želite li da aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> i dalje bude dopušten pristup podacima biometrijskih senzora na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g> kad je koristite?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Želite li aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dopustiti da vam šalje obavijesti?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Želite li aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> dopustiti da vam šalje obavijesti na uređaju <xliff:g id="DEVICE_NAME">%2$s</xliff:g>?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrolirana dopuštenja"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> ima pristup lokaciji"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Vaša organizacija dopušta da <xliff:g id="APP_NAME">%1$s</xliff:g> pristupa vašoj lokaciji"</string>
@@ -505,13 +544,14 @@
<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>
<string name="privdash_label_none" msgid="5991866260360484858">"Nijedno"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Posljednja\n24 sata"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"U posljednjih\n7 dana"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> posto"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> štiti Android. Budući da se vaši podaci obrađuju na ovom uređaju, upotreba dopuštenja te aplikacije ne prikazuje se na traci statusa ni na vašoj nadzornoj ploči za privatnost."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> štiti Android. Budući da se vaši podaci obrađuju na ovom uređaju, upotreba dopuštenja te aplikacije ne prikazuje se na vašoj nadzornoj ploči za privatnost."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera uređaja je blokirana"</string>
@@ -520,8 +560,15 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Za aplikacije i usluge"</string>
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Pristup kameri je isključen"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Pristup mikrofonu je isključen"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Pristup lokaciji je isključen"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Za aplikacije sustava za informiranje i zabavu"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Za obavezne aplikacije"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ova je aplikacija obavezna"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ovu aplikaciju zahtijeva proizvođač vašeg automobila"</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 +579,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 +624,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Ažuriranja o dijeljenju podataka"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Neke su aplikacije promijenile način na koji mogu dijeliti vaše podatke o lokaciji"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Postavke"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Pristupljeno: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pristupljeno jučer: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pristupljeno: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Vaša jednokratna zaporka je 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ograničena postavka"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Radi vaše sigurnosti ova postavka trenutačno nije dostupna."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je odbijen pristup dopuštenju <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zatražila pristup dopuštenju za osjetljive podatke koje može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog ograničenog dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je uskraćeno da prema zadanim postavkama bude <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zatražila pristup dopuštenjima za osjetljive podatke koja mogu ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tih ograničenih dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaciji je odbijen pristup"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Pristup tom dopuštenju može ugroziti vaše osobne i financijske podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Moguće je da aplikacija neće pravilno funkcionirati bez tog ograničenog dopuštenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saznajte kako omogućiti pristup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saznajte više"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"U redu"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Upit za dopuštenje je spriječen"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikacija zahtijeva dodatna dopuštenja, no dopuštenja se ne mogu odobriti u sesiji streaminga. Dopuštenje najprije odobrite na telefonu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za pozive ili poruke hitnim službama"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija poslana hitnim službama"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ova je aplikacija pristupila lokaciji vašeg uređaja prilikom upućivanja poziva ili poruke na broj hitne službe. To se može dogoditi čak i kad aplikacija nema dopuštenje za lokaciju ili je lokacija uređaja isključena. "<a href="https://support.google.com/android/answer/9319337">"Saznajte više"</a></string>
</resources>
diff --git a/PermissionController/res/values-hu-v34/strings.xml b/PermissionController/res/values-hu-v34/strings.xml
index 1a01b1cc0..b9c4af390 100644
--- a/PermissionController/res/values-hu-v34/strings.xml
+++ b/PermissionController/res/values-hu-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Az egészségügyi adatokhoz való alkalmazás-hozzáférés kezelése"</string>
<string name="location_settings" msgid="8863940440881290182">"Helyhozzáférés"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Alkalmazásoknál és szolgáltatásoknál. Ha ki van kapcsolva ez a beállítás, segélyhívó szám hívásakor a rendszer továbbra is megoszthatja a mikrofonadatokat."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Alkalmazásoknál és szolgáltatásoknál"</string>
</resources>
diff --git a/PermissionController/res/values-hu-watch/strings.xml b/PermissionController/res/values-hu-watch/strings.xml
index 6022530fa..54ef0998b 100644
--- a/PermissionController/res/values-hu-watch/strings.xml
+++ b/PermissionController/res/values-hu-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nem változtatható"</string>
<string name="generic_yes" msgid="2489207724988649846">"Igen"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Mégse"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Mindig"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Az app használata közben"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Mindig"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Az app használata közben"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Mindig"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Az app használata közben"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Mindig"</string>
</resources>
diff --git a/PermissionController/res/values-hu/strings.xml b/PermissionController/res/values-hu/strings.xml
index 12ad687a6..c5097f69e 100644
--- a/PermissionController/res/values-hu/strings.xml
+++ b/PermissionController/res/values-hu/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"engedélyek"</string>
<string name="cancel" msgid="8943320028373963831">"Mégse"</string>
<string name="back" msgid="6249950659061523680">"Vissza"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Bezárás"</string>
<string name="available" msgid="6007778121920339498">"Rendelkezésre áll"</string>
<string name="blocked" msgid="9195547604866033708">"Letiltva"</string>
<string name="on" msgid="280241003226755921">"Be"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Az összes engedély"</string>
<string name="other_permissions" msgid="2901186127193849594">"Egyéb alkalmazáslehetőségek"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Engedélykérés"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"A Wear nem támogatja a telepítés/eltávolítás műveletet."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Válassza ki, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mihez férjen hozzá"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"A(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; frissítése megtörtént. Válassza ki, hogy mihez férjen hozzá ez az alkalmazás."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Mégse"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Mindig az összes engedélyezése"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Mindig kérdezzen rá"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Tiltás"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Korlátozott hozzáférés engedélyezése"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Pontos hely"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Hozzávetőleges hely"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Pontos helyadatok használata"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Ha a pontos hely ki van kapcsolva, akkor az alkalmazások az Ön hozzávetőleges tartózkodási helyéhez férhetnek hozzá"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> – engedély"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Alkalmazás hozzáférése a következőhöz: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> – hozzáférés az alkalmazás számára a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> eszközön"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"A(z) <xliff:g id="APP">%1$s</xliff:g> összes engedélyének megtekintése"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Az ezzel az engedéllyel rendelkező összes alkalmazás megtekintése"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mikrofon Segéd általi használatának megjelenítése"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Engedélyek eltávolítása, ha nem használja az alkalmazást"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Engedélytörlés és tárhely-felszabadítás"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"App szüneteltetése, ha nem használja"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Nem használt alkalmazás kezelése"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Engedélyek eltávolítása, ideiglenes fájlok törlése és értesítések leállítása"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Engedélyek eltávolítása, ideiglenes fájlok törlése, értesítések leállítása és az alkalmazás archiválása"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Az adatok védelme érdekében az ennek az alkalmazásnak adott engedélyek visszavonásra kerülnek, ha néhány hónapon át nem használja az alkalmazást."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ha néhány hónapon át nem használja az alkalmazást, az adatok védelme érdekében a rendszer visszavonja a következő engedélyeket: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Az adatok védelme érdekében a rendszer eltávolította a néhány hónapja nem használt alkalmazások engedélyeit."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Engedélyezve az összes fájl kezeléséhez"</string>
<string name="ask_header" msgid="2633816846459944376">"Mindig kérdezzen rá"</string>
<string name="denied_header" msgid="903209608358177654">"Nem engedélyezett"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"További, minden fájlhoz hozzáférő alkalmazások megtekintése"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 nap}other{# nap}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# óra}other{# óra}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Jegyzetkészítő alkalmazás"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Alkalmazások, amelyek lehetővé teszik, hogy jegyzeteket készítsen az eszközén."</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"jegyzetek"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Alapértelmezett Wallet-app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet-app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"A Wallet-alkalmazások tárolhatják a hitel- és hűségkártyákat, az autókulcsokat és egyebeket a tranzakciók különböző formáinak támogatása érdekében."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> beállítása alapértelmezett Wallet-appként?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nincs szükség engedélyre"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Aktuális alapérték"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ne jelenjen meg többé"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Alapértelmezett"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"További alapértelmezések"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Linkek megnyitása"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Munkahelyi alapértelmezett"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Alapértelmezett a magánterületnél"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nincs"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Alapértelmezett)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nincs alkalmazás"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Segédet aktiváló parancsok észlelésének megjelenítése"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Ikon megjelenítése az állapotsoron, amikor a rendszer a mikrofont használja a hangsegéd aktiválásához"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszközön tárolt fotókhoz és médiatartalmakhoz?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen fotókhoz és médiatartalmakhoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a névjegyekhez?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a névjegyeihez ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszköz helyadataihoz?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a(z) &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; helyadataihoz?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Az alkalmazás csak akkor férhet hozzá a helyadatokhoz, amikor Ön használja az alkalmazást"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszköz helyadataihoz?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a(z) &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; helyadataihoz?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Előfordulhat, hogy az alkalmazás akkor is hozzá szeretne férni a helyadataihoz, amikor nem használja az alkalmazást. "<annotation id="link">"A beállításokban engedélyezheti."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Megváltoztatja a helyadatokhoz való hozzáférést a következő számára: &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Módosítja a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; helyadatokhoz való hozzáférését ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Az alkalmazás akkor is hozzá szeretne férni az Ön helyadataihoz, amikor Ön nem használja az alkalmazást. "<annotation id="link">"A beállításokban engedélyezheti."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"A(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; megkeresheti a közeli eszközöket, meghatározhatja relatív pozíciójukat, és csatlakozhat hozzájuk?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Megtalálhatja a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; a közeli eszközöket, csatlakozhat hozzájuk, és meghatározhatja a relatív pozíciójukat itt: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"A(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; megkeresheti a közeli eszközöket, meghatározhatja relatív pozíciójukat, és csatlakozhat hozzájuk? "<annotation id="link">"Engedélyezés a beállításokban."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Megváltoztatja a(z) <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> helyhozzáférését hozzávetőlegesről pontosra?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Módosítja a(z) <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> számára biztosított helyhozzáférést hozzávetőlegesről pontosra ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszköz hozzávetőleges helyadataihoz?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a(z) &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; hozzávetőleges helyéhez?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Pontos"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Hozzávetőleges"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a naptárhoz?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a naptárához ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy SMS-eket küldhessen és tekinthessen meg?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára az SMS-ek küldését és megtekintését ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszközön tárolt fotókhoz, médiatartalmakhoz és fájlokhoz?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy fotókhoz, médiatartalmakhoz és fájlokhoz férjen hozzá ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön lévő &lt;b&gt;fotókhoz, videókhoz, zenékhez és hangfájlokhoz&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön tárolt &lt;b&gt;fotókhoz, hang-, videó- és egyéb fájlokhoz&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön tárolt zenékhez és egyéb hanganyagokhoz?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a zenékhez ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Hozzáférhet a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; az eszközön tárolt fotókhoz és videókhoz?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a fotókhoz és a videókhoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az eszközön tárolt további fotókhoz és videókhoz?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy további fotókhoz és videókhoz férjen hozzá ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hangfelvételt készíthessen?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hanganyagot rögzítsen ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Az alkalmazás csak akkor tud majd hangfelvételt készíteni, amikor Ön használja az alkalmazást."</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hangfelvételt készíthessen?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hanganyagot rögzítsen ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Lehet, hogy az alkalmazás akkor is szeretne hangfelvételt készíteni, amikor Ön nem használja az alkalmazást. "<annotation id="link">"A beállításokban engedélyezheti."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Módosítja a mikrofonhoz való hozzáférést a következő számára: &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Módosítja a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mikrofonhoz való hozzáférését ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Az alkalmazás akkor is szeretne hangfelvételt készíteni, amikor Ön nem használja az alkalmazást. "<annotation id="link">"A beállításokban engedélyezheti."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára a testmozgási adataihoz való hozzáférést?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a testmozgásadatokhoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy képeket és videókat készíthessen?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Engedélyezi, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; képet készítsen és videót rögzítsen ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Az alkalmazás csak akkor tud majd fényképeket és videókat készíteni, amikor Ön használja az alkalmazást."</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy fényképeket és videókat készíthessen?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Engedélyezi, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; képet készítsen és videót rögzítsen ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Lehet, hogy az alkalmazás akkor is szeretne fotókat és videókat készíteni, amikor Ön nem használja az alkalmazást. "<annotation id="link">"A beállításokban engedélyezheti."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Módosítja a kamerához való hozzáférést a következő számára: &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Módosítja a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kamerához való hozzáférését ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Az alkalmazás akkor is szeretne fényképeket és videókat készíteni, amikor Ön nem használja az alkalmazást. "<annotation id="link">"A beállításokban engedélyezheti."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Engedélyezi, hogy a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; hozzáférjen az Ön hívásnaplóihoz?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen a telefonja hívásnaplóihoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hívásokat indíthasson és kezelhessen?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy telefonhívásokat indítson és kezeljen ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az életjelekkel kapcsolatos szenzoradatokhoz?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az életfunkciókkal kapcsolatos szenzoradatokhoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ez az alkalmazás akkor is hozzá szeretne férni az életjelekkel kapcsolatos szenzoradatokhoz, ha nincs használatban. A módosításhoz "<annotation id="link">"lépjen a beállításokhoz."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az életjelekkel kapcsolatos szenzoradatokhoz?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen az életfunkciókkal kapcsolatos szenzoradatokhoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Ha engedélyezni szeretné, hogy ez az alkalmazás mindig hozzáférjen a testérzékelők adataihoz (még akkor is, amikor nem használja), "<annotation id="link">"lépjen a beállításokhoz"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Továbbra is hozzáférhessen használat közben a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; alkalmazás a testérzékelők adataihoz?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Továbbra is engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy hozzáférjen testérzékelő-adatokhoz ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;, amíg az alkalmazás használatban van?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy értesítéseket küldjön Önnek?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Engedélyezi a(z) &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; számára, hogy értesítéseket küldjön Önnek ezen az eszközön: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Szabályozott engedélyek"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> hozzáfér a tartózkodási helyhez"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Szervezete lehetővé teszi a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> számára, hogy hozzáférjen az Ön tartózkodási helyéhez"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nincs"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Elmúlt\n24 óra"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Az elmúlt\nhét napban"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> százalék"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> Androiddal védett. Mivel adatainak feldolgozása ezen az eszközön történik, az alkalmazás engedélyhasználata nem jelenik meg az állapotsorban vagy az Ön adatvédelmi irányítópultján."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> Androiddal védett. Mivel adatainak feldolgozása ezen az eszközön történik, az alkalmazás engedélyhasználata nem jelenik meg az Ön adatvédelmi irányítópultján."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Az eszköz kamerája le van tiltva"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Alkalmazásoknál és szolgáltatásoknál"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Segélyhívó szám hívásakor a rendszer továbbra is megoszthatja a mikrofonadatokat."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Módosítás"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"A kamerához való hozzáférés ki van kapcsolva"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"A mikrofonhoz való hozzáférés ki van kapcsolva"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"A tartózkodási helyhez való hozzáférés ki van kapcsolva"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Infotainment típusú appok számára"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Szükséges alkalmazások számára"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ez egy szükséges alkalmazás"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ennek az alkalmazásnak a használatát előírja az autó gyártója"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Biztonság és adatvédelem"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Eszközvizsgálat"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Elvetés"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Adatmegosztási frissítések"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Egyes alkalmazások módosították, hogy miként oszthatják meg az Ön helyadatait"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Beállítások"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Hozzáférés: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Hozzáférés: tegnap, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Hozzáférés: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Az Ön egyszer használatos jelszava: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Korlátozott beállítás"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Biztonsága érdekében ez a beállítás jelenleg nem használható."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Az app nem kapott hozzáférést a következőhöz: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Az alkalmazás hozzáférési engedélyt kért a bizalmas adatokhoz, ami veszélybe sodorhatja az Ön személyes és pénzügyi adatait.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Lehetséges, hogy az alkalmazás nem működik megfelelően enélkül a korlátozott engedély nélkül. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;További információ a hozzáférés megadásának módjáról.&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Az app nem kapott hozzáférést a következőhöz: alapértelmezett <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Az alkalmazás hozzáférési engedélyt kért a bizalmas adatokhoz, ami veszélybe sodorhatja az Ön személyes és pénzügyi adatait.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Lehetséges, hogy az alkalmazás nem működik megfelelően ezen korlátozott engedélyek nélkül. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;További információ a hozzáférés megadásának módjáról.&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Az alkalmazás nem kapott hozzáférést"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Az ehhez az engedélyhez való hozzáférés veszélybe sodorhatja az Ön személyes és pénzügyi adatait.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Lehetséges, hogy az alkalmazás nem működik megfelelően enélkül a korlátozott engedély nélkül. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;További információ a hozzáférés megadásának módjáról.&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"További információ"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Engedélykérés letiltva"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ez az alkalmazás további engedélyeket kér, de az engedélyeket nem lehet megadni streamelési munkamenetben. Előbb adja meg az engedélyeket a telefonján."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"A segélyhívó szám felhívásakor vagy a segélyhívó számra való üzenetküldéskor"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"A helyadatok el lettek küldve a segélyhívó szolgálatnak"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Az alkalmazás hozzáfért az eszköze helyadataihoz a segélyhívó szám felhívása vagy a segélyhívó számra való üzenetküldés során. Ez akkor is megtörténhet, amikor az appnak nincs helymeghatározási jogosultsága, vagy ki vannak kapcsolva az eszköz helyadatai. "<a href="https://support.google.com/android/answer/9319337">"További információ"</a></string>
</resources>
diff --git a/PermissionController/res/values-hy-v34/strings.xml b/PermissionController/res/values-hy-v34/strings.xml
index fb8997eb1..79e7fc1a1 100644
--- a/PermissionController/res/values-hy-v34/strings.xml
+++ b/PermissionController/res/values-hy-v34/strings.xml
@@ -17,11 +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="location_settings" msgid="8863940440881290182">"Տեղորոշման թույլտվություն"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Հավելվածների և ծառայությունների համար։ Եթե այս կարգավորումն անջատված է, խոսափողի տվյալները միևնույն է կարող են փոխանցվել, երբ զանգեք արտակարգ իրավիճակների որևէ հեռախոսահամարի"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Հավելվածների և ծառայությունների համար"</string>
</resources>
diff --git a/PermissionController/res/values-hy-watch/strings.xml b/PermissionController/res/values-hy-watch/strings.xml
index 77f8f5afe..b62e2d035 100644
--- a/PermissionController/res/values-hy-watch/strings.xml
+++ b/PermissionController/res/values-hy-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Հնարավոր չէ փոխել"</string>
<string name="generic_yes" msgid="2489207724988649846">"Այո"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Չեղարկել"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Միշտ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Հավելվածն օգտագործելիս"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Միշտ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Հավելվածն օգտագործելիս"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Միշտ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Հավելվածն օգտագործելիս"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Միշտ"</string>
</resources>
diff --git a/PermissionController/res/values-hy/strings.xml b/PermissionController/res/values-hy/strings.xml
index 73742eb53..068096c08 100644
--- a/PermissionController/res/values-hy/strings.xml
+++ b/PermissionController/res/values-hy/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"թույլտվություններ"</string>
<string name="cancel" msgid="8943320028373963831">"Չեղարկել"</string>
<string name="back" msgid="6249950659061523680">"Հետ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Փակել"</string>
<string name="available" msgid="6007778121920339498">"Հասանելի է"</string>
<string name="blocked" msgid="9195547604866033708">"Արգելափակված է"</string>
<string name="on" msgid="280241003226755921">"Միացված է"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Տեղադրման/հեռացման գործողությունները Android Wear-ում չեն աջակցվում:"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Ընտրեք՝ ինչ թույլտվություններ եք ուզում տրամադրել &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածը թարմացվել է: Ընտրեք՝ ինչ թույլտվություններ եք ուզում տրամադրել այդ հավելվածին:"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Չեղարկել"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"Տրամադրել սահմանափակ հասանելիություն"</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_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_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g>ն օգտագործելու թույլտվություն այս հավելվածի համար <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Կառավարել հավելվածը, եթե չի օգտագործվում"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Հեռացնել թույլտվությունները, ջնջել ժամանակավոր ֆայլերը և դադարեցնել ծանուցումները"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Հեռացնել թույլտվությունները, ջնջել ժամանակավոր ֆայլերը, դադարեցնել ծանուցումները և արխիվացնել հավելվածը"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Եթե հավելվածը չի օգտագործվել մի քանի ամիս, դրա թույլտվությունները կհեռացվեն՝ ձեր տվյալները պաշտպանելու համար։"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Եթե հավելվածը չի օգտագործվել մի քանի ամիս, ձեր տվյալները պաշտպանելու համար հետևյալ թույլտվությունները կհեռացվեն՝ <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Հավելվածներից, որոնք մի քանի ամիս չեք օգտագործել, թույլտվությունները հեռացվել են՝ ձեր տվյալները պաշտպանելու համար։"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Հավելվածներ, որոնց հասանելի են բոլոր ֆայլերը"</string>
<string name="ask_header" msgid="2633816846459944376">"Ամեն անգամ հարցնել"</string>
<string name="denied_header" msgid="903209608358177654">"Արգելված"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքում"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Տեսնել այլ հավելվածներ, որոնց հասանելի են բոլոր ֆայլերը"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 օր}one{# օր}other{# օր}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ժամ}one{# ժամ}other{# ժամ}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Դրամապանակի կանխադրված հավելված"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Դրամապանակի հավելված"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Դրամապանակի հավելվածները կարող են պահել ձեր վարկային և մշտական հաճախորդի քարտերը, մեքենայի բանալիները և այլ բաներ, որոնք կհեշտացնեն ձեր գործարքները։"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Նշե՞լ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը որպես դրամապանակի կանխադրված հավելված"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Թույլտվություններ հարկավոր չեն"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Օգտագործվում է ըստ կանխադրման"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Նորից չհարցնել"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Նշել կանխադրված"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Այլ կանխադրված հավելվածներ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Հղումների բացում"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Կանխադրված՝ աշխատանքի համար"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Կանխադրված հավելվածներ մասնավոր տարածքի համար"</string>
<string name="default_app_none" msgid="9084592086808194457">"Չկա"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Համակարգի կանխադրված հավելված)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Հավելվածներ չկան"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել լուսանկարներ և մեդիա ֆայլեր &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր կոնտակտները &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Փոխե՞լ տեղորոշման թույլտվությունը &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածի համար &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; սարքում"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Հավելվածին անհրաժեշտ է ձեր գտնվելու վայրը հետագծելու թույլտվություն, նույնիսկ երբ դուք չեք օգտվում դրանից։ "<annotation id="link">"Թույլտվությունը տրամադրեք այստեղ"</annotation>"։"</string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքավորումը։"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքը։"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; սարքում թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքը"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին գտնել մոտակա սարքերը, միանալ դրանց և որոշել դրանց հարաբերական դիրքավորումը։ Թույլատրելու համար անցեք "<annotation id="link">"կարգավորումներ։"</annotation></string>
- <string name="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_device_aware_fineupgrade" msgid="4453775952305587571">"Թույլատրե՞լ <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> հավելվածին օգտագործել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր օրացույցը &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ուղարկել և կարդալ SMS հաղորդագրություններ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել լուսանկարներ, մեդիա ֆայլեր և այլ ֆայլեր &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել երաժշտական և աուդիո ֆայլեր &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել լուսանկարներ և տեսանյութեր &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել այլ լուսանկարներ և տեսանյութեր &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձայնագրել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ձայնագրել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Փոխե՞լ խոսափողի օգտագործման թույլտվությունը &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածի համար &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր ֆիզիկական ակտիվության տվյալները &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին լուսանկարել և տեսագրել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին լուսանկարել և տեսագրել &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Փոխե՞լ տեսախցիկի օգտագործման թույլտվությունը &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածի համար &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել հեռախոսի զանգերի մատյանը &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին կատարել հեռախոսազանգեր և կառավարել դրանք &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր մարմնի սենսորների տվյալները &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին օգտագործել ձեր մարմնի սենսորների տվյալները &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; սարքում"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Եթե ուզում եք, որ այս հավելվածին հասանելի լինեն մարմնի սենսորների տվյալները, նույնիսկ երբ չեք օգտվում հավելվածից, "<annotation id="link">"փոխեք կարգավորումները"</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_device_aware_sensors" msgid="5542771499929819675">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին աշխատանքի ընթացքում օգտագործել մարմնի սենսորների տվյալները &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Թույլատրե՞լ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; հավելվածին ուղարկել ձեզ ծանուցումներ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Չկա"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Վերջին\n24 ժամը"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Վերջին\n7 օրում"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> տոկոս"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը պաշտպանված է Android-ի կողմից։ Քանի որ ձեր տվյալներն այս սարքում են մշակվում, հավելվածի թույլտվությունների օգտագործումը չի ցուցադրվում թույլտվությունների կառավարման վահանակի կարգավիճակի գոտում։"</string>
<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>
@@ -520,7 +560,14 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Տեսախցիկի հասանելիությունն անջատված է"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Խոսափողի թույլտվությունն անջատված է"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Տեղորոշման թույլտվությունն անջատված է"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Տեղեկատվաժամանցային հավելվածների համար"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Պահանջվող հավելվածների համար"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Այս հավելվածը պարտադիր է"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Այս հավելվածը պահանջվում է ձեր մեքենայի արտադրողի կողմից"</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 +578,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Բացվել է <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Բացվել է երեկ, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Բացվել է <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Մեկանգամյա օգտագործման ձեր գաղտնաբառը՝ 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Սահմանափակումներով կարգավորում"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Անվտանգության նկատառումներից ելնելով՝ այս կարգավորումը ներկայումս անհասանելի է։"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Հավելվածին մերժվել է <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>ի օգտագործման թույլտվությունը"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Հավելվածը հայցել է կոնֆիդենցիալ տեղեկությունների օգտագործման թույլտվություն, որը կարող է վտանգի ենթարկել ձեր անձնական և ֆինանսական տեղեկությունները։<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Հնարավոր է, որ առանց այս սահմանափակված թույլտվության՝ հավելվածը չաշխատի պատշաճ կերպով։ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ինչպես տրամադրել տվյալների օգտագործման թույլտվություն&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Հավելվածին մերժվել է «<xliff:g id="ROLE_NAME">%1$s</xliff:g>» կատեգորիայում կանխադրված լինելու թույլտվությունը"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Հավելվածը հայցել է կոնֆիդենցիալ տեղեկությունների օգտագործման թույլտվություններ, որոնք կարող են վտանգի ենթարկել ձեր անձնական և ֆինանսական տեղեկությունները։<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Հնարավոր է, որ առանց այս սահմանափակված թույլտվությունների՝ հավելվածը չաշխատի պատշաճ կերպով։ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ինչպես տրամադրել տվյալների օգտագործման թույլտվություն&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Հավելվածին մերժվել է թույլտվությունը"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Եթե տրամադրեք այս թույլտվությունը, ձեր անձնական և ֆինանսական տեղեկությունները կարող են վտանգված լինել։<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Հնարավոր է, որ առանց այս սահմանափակված թույլտվության՝ հավելվածը չաշխատի պատշաճ կերպով։ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ինչպես տրամադրել տվյալների օգտագործման թույլտվություն&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Իմանալ ավելին"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Եղավ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Թույլտվության հայտն արգելափակվել է"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Այս հավելվածը պահանջում է լրացուցիչ թույլտվություններ, սակայն դրանք հնարավոր չէ տրամադրել հեռարձակման ժամանակ։ Նախ տրամադրեք թույլտվությունը ձեր հեռախոսում։"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Շտապ կանչի կամ հաղորդագրության համար"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Տեղադրության տվյալներն ուղարկվել են արտակարգ իրավիճակների ծառայություններին"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Այս հավելվածին հասանելի է դարձել ձեր սարքի տեղադրությունը արտակարգ իրավիճակների հեռախոսահամարին զանգելու կամ հաղորդագրություն ուղարկելու ժամանակ։ Նման բան կարող է պատահել, նույնիսկ եթե հավելվածը չունի տեղորոշման թույլտվություն, կամ սարքի տեղորոշումն անջատված է։ "<a href="https://support.google.com/android/answer/9319337">"Իմանալ ավելին"</a></string>
</resources>
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-v34/strings.xml b/PermissionController/res/values-in-v34/strings.xml
index 7e0c0a609..f6855807d 100644
--- a/PermissionController/res/values-in-v34/strings.xml
+++ b/PermissionController/res/values-in-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Kelola akses aplikasi ke data kesehatan"</string>
<string name="location_settings" msgid="8863940440881290182">"Akses lokasi"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Untuk aplikasi dan layanan. Jika setelan ini nonaktif, data mikrofon mungkin tetap dibagikan saat Anda menelepon nomor darurat"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Untuk aplikasi dan layanan"</string>
</resources>
diff --git a/PermissionController/res/values-in-watch/strings.xml b/PermissionController/res/values-in-watch/strings.xml
index 91c107aa0..c124feda4 100644
--- a/PermissionController/res/values-in-watch/strings.xml
+++ b/PermissionController/res/values-in-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Tidak dapat diubah"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ya"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Batal"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Semua"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Saat menggunakan aplikasi"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Semua"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Saat menggunakan aplikasi"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Semua"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Saat menggunakan aplikasi"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Semua"</string>
</resources>
diff --git a/PermissionController/res/values-in/strings.xml b/PermissionController/res/values-in/strings.xml
index 6c5eacd15..1a17b52c7 100644
--- a/PermissionController/res/values-in/strings.xml
+++ b/PermissionController/res/values-in/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"izin"</string>
<string name="cancel" msgid="8943320028373963831">"Batal"</string>
<string name="back" msgid="6249950659061523680">"Kembali"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Tutup"</string>
<string name="available" msgid="6007778121920339498">"Tersedia"</string>
<string name="blocked" msgid="9195547604866033708">"Diblokir"</string>
<string name="on" msgid="280241003226755921">"Aktif"</string>
@@ -34,6 +35,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>
@@ -60,6 +62,7 @@
<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 +73,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Semua izin"</string>
<string name="other_permissions" msgid="2901186127193849594">"Kemampuan aplikasi lainnya"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Permintaan izin"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Instal/Uninstal tidak didukung di Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Pilih item yang boleh diakses oleh &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; telah diperbarui. Pilih item yang boleh diakses oleh aplikasi ini."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Batal"</string>
@@ -127,7 +128,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>
@@ -191,12 +192,14 @@
<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>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Izinkan akses terbatas"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Lokasi presisi"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Perkiraan lokasi"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Gunakan lokasi presisi"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Saat lokasi presisi dinonaktifkan, aplikasi dapat mengakses perkiraan lokasi"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Izin <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Akses <xliff:g id="PERM">%1$s</xliff:g> untuk aplikasi ini"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Akses <xliff:g id="PERM">%1$s</xliff:g> untuk aplikasi ini di <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Lihat semua izin <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Lihat semua aplikasi yang memiliki izin ini"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Tampilkan penggunaan mikrofon Asisten"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Hapus izin jika aplikasi tidak digunakan"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Hapus izin &amp; kosongkan ruang penyimpanan"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Jeda aktivitas aplikasi jika tak dipakai"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Kelola aplikasi jika tidak digunakan"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Hapus izin dan file sementara, serta hentikan notifikasi"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Hapus izin dan file sementara, hentikan notifikasi, dan arsipkan aplikasi"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Untuk melindungi data Anda, izin aplikasi ini akan dihapus jika aplikasi tidak digunakan dalam beberapa bulan."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Untuk melindungi data Anda, izin berikut akan dihapus jika aplikasi tidak digunakan dalam beberapa bulan: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Untuk melindungi data Anda, izin dari aplikasi yang tidak digunakan dalam beberapa bulan telah dihapus."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Diizinkan untuk mengelola semua file"</string>
<string name="ask_header" msgid="2633816846459944376">"Selalu tanya"</string>
<string name="denied_header" msgid="903209608358177654">"Tidak diizinkan"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> di <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Lihat aplikasi lain yang dapat mengakses semua file"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 hari}other{# hari}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# jam}other{# jam}}"</string>
@@ -357,12 +363,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikasi catatan"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikasi untuk membuat catatan di perangkat"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"catatan"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Aplikasi dompet default"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikasi dompet"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikasi dompet dapat menyimpan kartu kredit dan kartu loyalitas, kunci mobil, serta hal lain untuk membantu berbagai bentuk transaksi."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Setel <xliff:g id="APP_NAME">%1$s</xliff:g> sebagai aplikasi dompet default?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Tidak ada izin yang diperlukan"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Default saat ini"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Jangan tanya lagi"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Jadikan default"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Default lainnya"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Membuka link"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Default untuk kerja"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Default untuk ruang privasi"</string>
<string name="default_app_none" msgid="9084592086808194457">"Tidak ada"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Default sistem)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Tidak ada aplikasi"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Tampilkan deteksi pemicu asisten"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Tampilkan ikon di status bar saat mikrofon digunakan untuk mengaktifkan asisten suara"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan media di perangkat?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan media di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kontak?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kontak Anda di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi perangkat ini?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikasi ini hanya akan memiliki akses ke lokasi selagi Anda menggunakan aplikasi"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi perangkat ini?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Aplikasi ini mungkin ingin selalu mengakses lokasi, meski tidak sedang digunakan. "<annotation id="link">"Izinkan di setelan."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Ubah akses lokasi untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Ubah akses lokasi untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Aplikasi ini mungkin ingin selalu mengakses lokasi, meski tidak sedang digunakan. "<annotation id="link">"Izinkan di setelan."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menemukan, terhubung ke, dan menentukan posisi relatif perangkat di sekitar?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menemukan, menghubungkan, dan menentukan posisi relatif perangkat di sekitar di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menemukan, terhubung ke, dan menentukan posisi relatif perangkat di sekitar? "<annotation id="link">"Izinkan di setelan."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Ubah akses lokasi <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> dari perkiraan ke lokasi presisi?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Ubah akses lokasi <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; dari perkiraan menjadi presisi?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses perkiraan lokasi perangkat ini?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses perkiraan lokasi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Presisi"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Perkiraan"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kalender?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kalender Anda di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengirim dan melihat SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengirim dan melihat pesan SMS di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto, media, dan file di perangkat?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto, media, dan file di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses &lt;b&gt;foto, video, musik, dan audio&lt;/b&gt; di perangkat ini?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses &lt;b&gt;foto, video, musik, audio, dan file lainnya&lt;/b&gt; di perangkat ini?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses musik dan audio di perangkat ini?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses musik dan audio di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video di perangkat ini?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video lainnya di perangkat ini?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lebih banyak foto dan video di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikasi hanya dapat merekam audio saat aplikasi sedang digunakan"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merekam audio di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Aplikasi ini mungkin ingin selalu merekam audio, meski aplikasi tidak sedang digunakan. "<annotation id="link">"Izinkan di setelan"</annotation>"."</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Ubah akses mikrofon untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Ubah akses mikrofon untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Aplikasi ini ingin selalu merekam audio, meski aplikasi tidak sedang digunakan. "<annotation id="link">"Izinkan di setelan"</annotation>"."</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses aktivitas fisik Anda?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses aktivitas fisik Anda di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merekam video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil foto dan merekam video di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikasi hanya dapat mengambil gambar dan merekam video saat aplikasi sedang digunakan"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merekam video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil foto dan merekam video di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Aplikasi ini mungkin ingin selalu mengambil gambar dan merekam video, meski aplikasi tidak sedang digunakan. "<annotation id="link">"Izinkan di setelan"</annotation>"."</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Ubah akses kamera untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Ubah akses kamera untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Aplikasi ini ingin selalu mengambil gambar dan merekam video, meski aplikasi tidak sedang digunakan. "<annotation id="link">"Izinkan di setelan"</annotation>"."</string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses log panggilan telepon?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses log panggilan telepon Anda di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; melakukan dan mengelola panggilan telepon?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; untuk melakukan dan mengelola panggilan telepon di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data sensor tentang tanda-tanda vital Anda?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data sensor terkait tanda-tanda vital Anda di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplikasi ini ingin selalu mengakses data sensor tentang tanda-tanda vital Anda, meski aplikasi tidak sedang digunakan. Untuk melakukan perubahan ini, "<annotation id="link">"buka setelan"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Izinkan &lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data sensor tentang tanda-tanda vital Anda?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data sensor terkait tanda-tanda vital Anda di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Untuk selalu mengizinkan aplikasi ini mengakses data sensor tubuh, meski saat aplikasi tidak sedang digunakan, "<annotation id="link">"buka setelan."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Terus izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data sensor tubuh saat aplikasi sedang digunakan?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Tetap izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data sensor tubuh di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; saat aplikasi sedang digunakan?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengirim notifikasi kepada Anda?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Izinkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengirimi Anda notifikasi di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Izin terkontrol"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> memiliki akses lokasi"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organisasi Anda mengizinkan <xliff:g id="APP_NAME">%1$s</xliff:g> mengakses lokasi Anda"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Tidak ada"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 jam\nterakhir"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 hari\nterakhir"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g><xliff:g id="PERCENT">%2$d</xliff:g> persen"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilindungi dengan Android. Karena data Anda diproses di perangkat ini, penggunaan izin aplikasi ini tidak ditampilkan di status bar atau dasbor privasi Anda."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilindungi dengan Android. Karena data Anda diproses di perangkat ini, penggunaan izin aplikasi ini tidak ditampilkan di dasbor privasi Anda."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera perangkat diblokir"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Untuk aplikasi dan layanan"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Data mikrofon mungkin tetap dibagikan saat Anda menelepon nomor darurat."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Ubah"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Akses kamera nonaktif"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Akses mikrofon nonaktif"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Akses lokasi nonaktif"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Untuk aplikasi infotainmen"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Untuk aplikasi yang diperlukan"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Aplikasi ini diperlukan"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Aplikasi ini diperlukan produsen mobil Anda"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Keamanan &amp; privasi"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Pindai perangkat"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Tutup"</string>
@@ -607,7 +654,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Pembaruan berbagi data"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Beberapa aplikasi mengubah caranya berbagi data lokasi Anda"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Setelan"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Diakses <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Diakses kemarin <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Diakses <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Sandi sekali pakai Anda adalah 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Setelan terbatas"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Demi keamanan Anda, setelan ini tidak tersedia untuk saat ini."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikasi ditolak aksesnya ke <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikasi meminta akses ke izin sensitif yang dapat membahayakan info pribadi dan keuangan Anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikasi mungkin tidak dapat berfungsi dengan baik tanpa izin terbatas ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtPelajari cara mengizinkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikasi ditolak aksesnya untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> default"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikasi meminta akses ke izin sensitif yang dapat membahayakan info pribadi dan keuangan Anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikasi mungkin tidak dapat berfungsi dengan baik tanpa izin terbatas ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtPelajari cara mengizinkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikasi ditolak aksesnya"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Akses ke izin ini dapat membahayakan info pribadi dan keuangan Anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikasi mungkin tidak dapat berfungsi dengan baik tanpa izin terbatas ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtPelajari cara mengizinkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Pelajari lebih lanjut"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Oke"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permintaan izin diblokir"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplikasi ini meminta izin tambahan, tetapi izin tidak dapat diberikan dalam sesi streaming. Berikan izin di ponsel terlebih dahulu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Untuk pesan teks atau panggilan darurat"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokasi dikirim ke layanan darurat"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Aplikasi ini mengakses lokasi perangkat Anda saat melakukan panggilan atau mengirim pesan teks ke nomor darurat. Hal ini dapat terjadi bahkan saat aplikasi tidak memiliki izin akses lokasi atau lokasi perangkat dinonaktifkan. "<a href="https://support.google.com/android/answer/9319337">"Pelajari lebih lanjut"</a></string>
</resources>
diff --git a/PermissionController/res/values-is-v34/strings.xml b/PermissionController/res/values-is-v34/strings.xml
index b897bcc71..11b5828c4 100644
--- a/PermissionController/res/values-is-v34/strings.xml
+++ b/PermissionController/res/values-is-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Stjórna aðgangi forrita að heilsufarsgögnum"</string>
<string name="location_settings" msgid="8863940440881290182">"Aðgangur að staðsetningu"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Fyrir forrit og þjónustur. Þegar slökkt er á þessari stillingu verður hljóðnemagögnum þó hugsanlega deilt þegar þú hringir í neyðarnúmer"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Fyrir forrit og þjónustur"</string>
</resources>
diff --git a/PermissionController/res/values-is-watch/strings.xml b/PermissionController/res/values-is-watch/strings.xml
index 7f01f675c..6bfd6c7f4 100644
--- a/PermissionController/res/values-is-watch/strings.xml
+++ b/PermissionController/res/values-is-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Má ekki breyta"</string>
<string name="generic_yes" msgid="2489207724988649846">"Já"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Hætta við"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Alltaf"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Þegar forrit er notað"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Alltaf"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Þegar forrit er notað"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Alltaf"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Þegar forrit er notað"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Alltaf"</string>
</resources>
diff --git a/PermissionController/res/values-is/strings.xml b/PermissionController/res/values-is/strings.xml
index ab698d6ab..4fb572f4e 100644
--- a/PermissionController/res/values-is/strings.xml
+++ b/PermissionController/res/values-is/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"heimildir"</string>
<string name="cancel" msgid="8943320028373963831">"Hætta við"</string>
<string name="back" msgid="6249950659061523680">"Til baka"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Tiltækt"</string>
<string name="blocked" msgid="9195547604866033708">"Lokað á"</string>
<string name="on" msgid="280241003226755921">"Kveikt"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Allar heimildir"</string>
<string name="other_permissions" msgid="2901186127193849594">"Aðrir forritseiginleikar"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Beiðni um heimild"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Aðgerðir til að setja upp / fjarlægja eru ekki studdar í Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Veldu hverju &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fær aðgang að"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; hefur verið uppfært. Veldu hverju forritið fær aðgang að."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Hætta við"</string>
@@ -191,12 +193,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Alltaf leyfa allt"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Spyrja alltaf"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ekki leyfa"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Leyfa takmarkaðan aðgang"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Nákvæm staðsetning"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Áætluð staðsetning"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Nota nákvæma staðsetningu"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Þegar slökkt er á nákvæmri staðsetningu hafa forrit aðgang að áætlaðri staðsetningu"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> - heimild"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>: Aðgangur fyrir þetta forrit"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Aðgangur að <xliff:g id="PERM">%1$s</xliff:g> fyrir þetta forrit í <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Sjá allar heimildir fyrir „<xliff:g id="APP">%1$s</xliff:g>“"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Sjá öll forrit með þessa heimild"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Sýna hljóðnemanotkun hjálpara"</string>
@@ -204,7 +208,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Fjarlægja heimildir ef forrit er ekki notað"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Fjarlægja heimildir og losa um pláss"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Gera hlé á forritavirkni ef ekki notað"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Stjórna forriti ef það er ónotað"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Fjarlægja heimildir, eyða tímabundnum skrám og stöðva tilkynningar"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Fjarlægja heimildir, eyða tímabundnum skrám, stöðva tilkynningar og setja forritið í geymslu"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Til að vernda gögnin þín verða heimildir þessa forrits fjarlægðar ef það er ekki notað í nokkra mánuði."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Til að vernda gögnin þín verða eftirfarandi heimildir fjarlægðar ef forritið er ekki notað í nokkra mánuði: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Til að vernda gögnin þín voru heimildir fjarlægðar úr forritum sem þú hefur ekki notað í nokkra mánuði."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Leyfði stjórnun á öllum skrám"</string>
<string name="ask_header" msgid="2633816846459944376">"Spyrja alltaf"</string>
<string name="denied_header" msgid="903209608358177654">"Ekki heimilað"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> í <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Sjá fleiri forrit sem geta opnað allar skrár"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dagur}one{# dagur}other{# dagar}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# klukkustund}one{# klukkustund}other{# klukkustundir}}"</string>
@@ -294,7 +301,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 +382,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>
@@ -401,6 +408,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Glósuforrit"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Forrit sem gera þér kleift að taka glósur í tækinu þínu"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"glósur"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Sjálfgefið veskisforrit"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Veskisforrit"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Veskisforrit geta geymt kredit- og tryggðarkortin þín, bíllyklana og aðra hluti til að auðvelda þér að ganga frá ýmiskonar greiðslum."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Stilla <xliff:g id="APP_NAME">%1$s</xliff:g> sem sjálfgefið veskisforrit?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Engra heimilda krafist"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Núverandi sjálfgefið forrit"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ekki spyrja aftur"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Velja sem sjálfgefið"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Fleiri sjálfgildi"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Opnun tengla"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Sjálfgefið fyrir vinnu"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Sjálfgefið fyrir leynirými"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ekkert"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Sjálfgildi kerfis)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Engin forrit"</string>
@@ -455,48 +468,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Sýna virkjunarkennsl hjálpara"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Sýna tákn á stöðustiku þegar hljóðnemi er notaður til að ræsa raddaðstoð"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum og efni í tækinu?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum og efni í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að tengiliðunum þínum?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að tengiliðum þínum í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að staðsetningu þessa tækis?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að staðsetningu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Forritið hefur aðeins aðgang að staðsetningunni á meðan þú notar forritið"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að staðsetningu þessa tækis?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að staðsetningu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Þetta forrit gæti beðið um aðgang að staðsetningu þinni öllum stundum, jafnvel þegar þú ert ekki að nota forritið. "<annotation id="link">"Þú getur leyft það í stillingum."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Viltu breyta aðgangi að staðsetningu fyrir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Breyta staðsetningaraðgangi fyrir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Þetta forrit vill fá aðgang að staðsetningu þinni öllum stundum, jafnvel þegar þú ert ekki að nota forritið. "<annotation id="link">"Þú getur leyft það í stillingum."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að finna, tengjast við og ákvarða fjarlægð milli nálægra tækja?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að finna, tengjast og greina áætlaða staðsetningu nálægra tækja í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að finna, tengjast við og ákvarða fjarlægð milli nálægra tækja? "<annotation id="link">"Þú getur leyft það í stillingunum."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Breyta aðgangi <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> að staðsetningu úr áætlaðri í nákvæma?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Breyta staðsetningaraðgangi <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> úr áætluðum í nákvæman í: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að áætlaðri staðsetningu þessa tækis?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að áætlaðri staðsetningu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;’s?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Nákvæm"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Áætluð"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að dagatalinu þínu?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að dagatalinu þínu í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að senda og skoða SMS-skilaboð?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að senda og skoða SMS-skilaboð í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum, efni og skrám í tækinu?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum, efni og skrám í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að &lt;b&gt;myndum, myndskeiðum, tónlist og hljóði&lt;/b&gt; í þessu tæki?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að &lt;b&gt;myndum, myndskeiðum, tónlist, hljóði og öðrum skrám&lt;/b&gt; í þessu tæki?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að tónlist og hljóði í þessu tæki?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að tónlist og hljóði í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum og myndskeiðum í þessu tæki?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að myndum og vídeóum í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að fleiri myndum og myndskeiðum í þessu tæki?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að fleiri myndum og vídeóum í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð á &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Forritið mun aðeins geta tekið upp hljóð þegar þú ert að nota forritið"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka upp hljóð á &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Þetta forrit gæti viljað taka upp hljóð hvenær sem er, jafnvel þegar þú ert ekki að nota forritið. "<annotation id="link">"Þú getur leyft það í stillingum."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Viltu breyta aðgangi að hljóðnema fyrir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Breyta hljóðnemaaðgangi fyrir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Þetta forrit vill taka upp hljóð hvenær sem er, jafnvel þegar þú ert ekki að nota forritið. "<annotation id="link">"Þú getur leyft það í stillingum."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að fá aðgang að hreyfingu þinni?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að gögnum um hreyfingu þína í: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka myndir og myndskeið?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka myndir og taka upp vídeó á &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Forritið mun aðeins geta tekið myndir og tekið upp myndskeið þegar þú ert að nota forritið"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka myndir og myndskeið?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að taka myndir og taka upp vídeó á &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Þetta forrit gæti viljað taka myndir og taka upp myndskeið hvenær sem er, jafnvel þegar þú ert ekki að nota forritið. "<annotation id="link">"Þú getur leyft það í stillingum."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Viltu breyta aðgangi að myndavél fyrir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Breyta myndavélaraðgangi fyrir &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Þetta forrit vill taka myndir og taka upp myndskeið hvenær sem er, jafnvel þegar þú ert ekki að nota forritið. "<annotation id="link">"Þú getur leyft það í stillingum."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að símtalaskrám símans?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að símtalaskrám þínum í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Viltu leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að hringja og stjórna símtölum?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að hringja úr og stjórna símtölum í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að skynjaragögnum um lífsmörk þín?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að skynjaragögnum um lífsmörk þín í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Þetta forrit vill fá stöðugan aðgang að skynjaragögnum um lífsmörk þín, líka þegar þú ert ekki að nota forritið. Ef þú vilt gera þessa breytingu skaltu "<annotation id="link">"opna stillingar."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Viltu veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að skynjaragögnum um lífsmörk þín?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að skynjaragögnum um lífsmörk þín í: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Til að veita þessu forriti samfelldan aðgang að gögnum líkamsskynjara skaltu "<annotation id="link">"opna stillingarnar."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; áfram aðgang að gögnum líkamsskynjara á meðan forritið er í notkun?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Halda áfram að veita &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aðgang að líkamsskynjaragögnum í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; á meðan forrit er í notkun?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að senda þér tilkynningar?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Leyfa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; að senda þér tilkynningar í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Stýrðar heimildir"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> er með staðsetningaraðgang"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Fyrirtækið þitt veitir <xliff:g id="APP_NAME">%1$s</xliff:g> aðgang að staðsetningu þinni"</string>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ekkert"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Síðasta\nsólarhringinn"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Síðastliðna\n7 daga"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> prósent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> er verndað af Android. Unnið er úr gögnum þínum í þessu tæki og þar af leiðandi birtist heimildarnotkun þessa forrits ekki á stöðustikunni eða persónuverndarstjórnborðinu."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> er verndað af Android. Unnið er úr gögnum þínum í þessu tæki og þar af leiðandi birtist heimildarnotkun þessa forrits ekki á persónuverndarstjórnborðinu þínu."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Lokað er fyrir myndavél tækis"</string>
@@ -520,6 +561,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Fyrir forrit og þjónustu"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Hljóðnemagögnum er þó hugsanlega deilt þegar hringt er í neyðarnúmer."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Breyta"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Slökkt er á aðgangi að myndavél"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Slökkt er á aðgangi að hljóðnema"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Slökkt er á aðgangi að staðsetningu"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Fyrir upplýsinga- og afþreyingarforrit"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Fyrir áskilin forrit"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Þetta forrit er áskilið"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Framleiðandi bílsins krefst þessa forrits"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Öryggi og persónuvernd"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skanna tæki"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Hunsa"</string>
@@ -619,4 +667,26 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Gagnadeilingaruppfærslur"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Deiling staðsetningargagna hefur breyst í sumum forritum"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Stillingar"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Opnað kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Opnað í gær kl. <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Opnað <xliff:g id="TIME_DATE_0">%1$s</xliff:g> kl. <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Einnota aðgangsorðið þitt er 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Takmörkuð stilling"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Til að tryggja öryggi þitt er þessi stilling ekki tiltæk eins og er."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Forritið fékk ekki aðgang að <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Forritið bað um aðgang að heimild fyrir viðkvæmu efni sem getur stofnað persónu- og fjármálaupplýsingum þínum í hættu.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Forritið virkar hugsanlega ekki sem skyldi án þessarar takmörkuðu heimildar. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kynntu þér hvernig þú leyfir aðgang&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Forritið fékk ekki aðgang að sjálfgefnu <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Forritið bað um aðgang að heimildum fyrir viðkvæmu efni sem getur stofnað persónu- og fjármálaupplýsingum þínum í hættu.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Forritið virkar hugsanlega ekki sem skyldi án þessara takmörkuðu heimilda. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kynntu þér hvernig þú leyfir aðgang&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Forritið fékk ekki aðgang"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Aðgangur að þessari heimild getur stofnað persónu- og fjármálaupplýsingum þínum í hættu.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Forritið virkar hugsanlega ekki sem skyldi án þessarar takmörkuðu heimildar. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Kynntu þér hvernig þú leyfir aðgang&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Nánar"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Í lagi"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Heimildarbeiðni hafnað"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Þetta forrit biður um viðbótarheimildir en ekki er hægt að veita heimildir í streymislotu. Veittu heimildina í símanum fyrst."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-it-v34/strings.xml b/PermissionController/res/values-it-v34/strings.xml
index 27da7e634..77564456c 100644
--- a/PermissionController/res/values-it-v34/strings.xml
+++ b/PermissionController/res/values-it-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Gestisci l\'accesso delle app ai dati sulla salute"</string>
<string name="location_settings" msgid="8863940440881290182">"Accesso alla posizione"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Per app e servizi. Se questa impostazione viene disattivata, i dati del microfono potrebbero comunque essere condivisi quando chiami un numero di emergenza"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Per app e servizi"</string>
</resources>
diff --git a/PermissionController/res/values-it-watch/strings.xml b/PermissionController/res/values-it-watch/strings.xml
index 6cb0ce756..7e5106952 100644
--- a/PermissionController/res/values-it-watch/strings.xml
+++ b/PermissionController/res/values-it-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Impos. modificare"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sì"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Annulla"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Sempre"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Con app in uso"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Sempre"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Con app in uso"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Sempre"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Con app in uso"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Sempre"</string>
</resources>
diff --git a/PermissionController/res/values-it/strings.xml b/PermissionController/res/values-it/strings.xml
index 4d653a874..b6cbdf9b3 100644
--- a/PermissionController/res/values-it/strings.xml
+++ b/PermissionController/res/values-it/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"autorizzazioni"</string>
<string name="cancel" msgid="8943320028373963831">"Annulla"</string>
<string name="back" msgid="6249950659061523680">"Indietro"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Chiudi"</string>
<string name="available" msgid="6007778121920339498">"Disponibile"</string>
<string name="blocked" msgid="9195547604866033708">"Bloccato"</string>
<string name="on" msgid="280241003226755921">"On"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Tutte le autorizzazioni"</string>
<string name="other_permissions" msgid="2901186127193849594">"Altre funzionalità dell\'app"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Richiesta di autorizzazione"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Le azioni di installazione/disinstallazione non sono supportate su Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Scegli i dati a cui l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; può accedere"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"L\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; è stata aggiornata. Scegli i dati a cui può accedere."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Annulla"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Consenti sempre tutti"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Chiedi ogni volta"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Non consentire"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Consenti accesso limitato"</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">"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>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> accesso per questa app su <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Mostra tutte le autorizzazioni di <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Mostra tutte le app con questa autorizzazione"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostra utilizzo microfono dell\'assistente"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Rimuovi autorizzazioni se non in uso"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Rimuovi autorizzazioni e libera spazio"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Sospendi attività app se inutilizzata"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gestisci l\'app se inutilizzata"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Rimuovi le autorizzazioni, elimina i file temporanei e interrompi le notifiche"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Rimuovi le autorizzazioni, elimina i file temporanei, interrompi le notifiche e archivia l\'app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Per proteggere i tuoi dati, le autorizzazioni di questa app verranno rimosse se l\'app non viene usata per alcuni mesi."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Per proteggere i tuoi dati, se l\'app non viene usata per alcuni mesi, le seguenti autorizzazioni verranno rimosse: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Per proteggere i tuoi dati, sono state rimosse le autorizzazioni dalle app che non hai utilizzato per alcuni mesi."</string>
@@ -252,6 +257,7 @@
<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="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> su <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"App per le note"</string>
<string name="role_notes_description" msgid="8496852798616883551">"App che ti permettono di prendere appunti sul tuo dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"note"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"App portafoglio predefinita"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"App portafoglio"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Le app portafoglio possono memorizzare carte di credito e fedeltà, chiavi della macchina e altro per facilitare varie forme di transazioni."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Impostare <xliff:g id="APP_NAME">%1$s</xliff:g> come app portafoglio predefinita?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nessuna autorizzazione necessaria"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Valore predefinito attuale"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Non chiedermelo più"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Imposta predefinito"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Altre app predefinite"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Apertura link"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predefinite per il lavoro"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Predefinite per lo spazio privato"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nessuna"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Predefinita)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nessuna app"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostra il rilevamento dell\'attivazione dell\'assistente"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostra l\'icona nella barra di stato quando viene usato il microfono per attivare l\'assistente vocale"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alle foto e ai contenuti multimediali sul tuo dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto e contenuti multimediali su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai tuoi contatti?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai contatti su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di questo dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"L\'app avrà accesso alla posizione soltanto quando la usi"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di questo dispositivo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Cambiare l\'accesso alla posizione per l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di trovare e connettersi ai dispositivi nelle vicinanze, oltre a stabilirne la posizione relativa, su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"Modificare l\'accesso alla posizione di <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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_device_aware_coarselocation" msgid="8367540370912066757">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla posizione approssimativa di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Esatta"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Approssimativa"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere al tuo calendario?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere al tuo calendario su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di inviare e visualizzare SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di inviare e visualizzare SMS su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto, contenuti multimediali e file memorizzati sul dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto, contenuti multimediali e file su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a &lt;b&gt;foto, video, musica e audio&lt;/b&gt; sul dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a &lt;b&gt;foto, video, musica, audio e altri file&lt;/b&gt; sul dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a musica e audio sul dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a musica e audio su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto e video sul dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere a foto e video su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ad altri video e foto sul dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ad altri video e foto su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"L\'app potrà registrare audio soltanto quando la usi"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vuoi consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di registrare audio su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Questa app potrebbe voler registrare sempre audio, anche quando non la usi. "<annotation id="link">"Consenti l\'accesso nelle impostazioni."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Vuoi cambiare l\'accesso al microfono per l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Cambiare l\'accesso al microfono per l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Questa app vorrebbe registrare sempre audio, anche quando non la usi. "<annotation id="link">"Consenti l\'accesso nelle impostazioni."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla tua attività fisica?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere alla tua attività fisica su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di scattare foto e registrare video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di scattare foto e registrare video su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"L\'app potrà scattare foto e registrare video soltanto quando la usi"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vuoi consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di scattare foto e registrare video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di scattare foto e registrare video su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Cambiare l\'accesso alla fotocamera per l\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai registri chiamate dello smartphone su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di effettuare e gestire telefonate su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"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 di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"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 di &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Continuare a consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di accedere ai dati dei sensori del corpo mentre l\'app è in uso?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Continuare a consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ad accedere ai dati dei sensori del corpo mentre è in uso su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Consentire all\'app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di inviarti notifiche?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Consentire a &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; di inviarti notifiche su &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Autorizzazioni controllate"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> ha accesso alla posizione"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"La tua organizzazione consente all\'app <xliff:g id="APP_NAME">%1$s</xliff:g> di accedere alla tua posizione"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nessuna"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Ultime\n24 ore"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Ultimi\n7 giorni"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> percento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> è protetta da Android. I tuoi dati vengono elaborati su questo dispositivo, pertanto l\'utilizzo delle autorizzazioni di questa app non viene mostrato nella barra di stato o nella dashboard della privacy."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> è protetta da Android. I tuoi dati vengono elaborati su questo dispositivo, pertanto l\'utilizzo delle autorizzazioni di questa app non viene mostrato nella dashboard della privacy."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"La fotocamera del dispositivo è bloccata"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Per app e servizi"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"I dati del microfono potrebbero comunque essere condivisi quando chiami un numero di emergenza."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Cambia"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Accesso fotocamera disattivato"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Accesso al microfono disattivato"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Accesso alla posizione disattivato"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Per app di infotainment"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Per app obbligatorie"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Questa app è obbligatoria"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Questa app è richiesta dal produttore della tua auto"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sicurezza e privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analizza il dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ignora"</string>
@@ -619,4 +666,23 @@
<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>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Impostazioni"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Ultimo accesso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Ultimo accesso ieri: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Ultimo accesso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"La password monouso è: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Impostazione con limitazioni"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Per la tua sicurezza, questa impostazione non è al momento disponibile."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"All\'app è stato negato l\'accesso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"L\'app ha richiesto l\'accesso a un\'autorizzazione sensibile che può mettere a rischio le tue informazioni finanziarie e personali.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>È possibile che l\'app non funzioni correttamente senza questa autorizzazione limitata. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Scopri di più su come consentire l\'accesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"All\'app è stato negato l\'accesso al ruolo <xliff:g id="ROLE_NAME">%1$s</xliff:g> predefinita"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"L\'app ha richiesto l\'accesso ad autorizzazioni sensibili che possono mettere a rischio le tue informazioni finanziarie e personali.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>È possibile che l\'app non funzioni correttamente senza queste autorizzazioni limitate. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Scopri di più su come consentire l\'accesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"All\'app è stato negato l\'accesso"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"L\'accesso a questa autorizzazione può mettere a rischio le tue informazioni finanziarie e personali.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>È possibile che l\'app non funzioni correttamente senza questa autorizzazione limitata. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Scopri di più su come consentire l\'accesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Scopri di più"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Ok"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Richiesta di autorizzazione rifiutata"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Questa app richiede autorizzazioni aggiuntive, ma non è possibile concedere autorizzazioni in una sessione di streaming. Devi prima concedere l\'autorizzazione sul tuo smartphone."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Per messaggio o chiamata di emergenza"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Posizione inviata ai servizi di emergenza"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Questa app ha eseguito l\'accesso alla posizione del tuo dispositivo durante una chiamata o l\'invio di un messaggio a un numero di emergenza. Questo può accadere anche quando l\'app non ha l\'autorizzazione di accesso alla posizione o se la localizzazione del dispositivo è disattivata. "<a href="https://support.google.com/android/answer/9319337">"Scopri di più"</a></string>
</resources>
diff --git a/PermissionController/res/values-iw-v34/strings.xml b/PermissionController/res/values-iw-v34/strings.xml
index 370a20dcb..61e486e81 100644
--- a/PermissionController/res/values-iw-v34/strings.xml
+++ b/PermissionController/res/values-iw-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-iw-watch/strings.xml b/PermissionController/res/values-iw-watch/strings.xml
index 3db39b59b..bdf113146 100644
--- a/PermissionController/res/values-iw-watch/strings.xml
+++ b/PermissionController/res/values-iw-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"לא ניתן לשנות"</string>
<string name="generic_yes" msgid="2489207724988649846">"כן"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ביטול"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"כל הזמן"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"בזמן השימוש באפליקציה"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"כל הזמן"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"בזמן השימוש באפליקציה"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"כל הזמן"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"בזמן השימוש באפליקציה"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"כל הזמן"</string>
</resources>
diff --git a/PermissionController/res/values-iw/strings.xml b/PermissionController/res/values-iw/strings.xml
index f261a0552..bb35a88a3 100644
--- a/PermissionController/res/values-iw/strings.xml
+++ b/PermissionController/res/values-iw/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"הרשאות"</string>
<string name="cancel" msgid="8943320028373963831">"ביטול"</string>
<string name="back" msgid="6249950659061523680">"חזרה"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"סגירה"</string>
<string name="available" msgid="6007778121920339498">"הגישה מותרת"</string>
<string name="blocked" msgid="9195547604866033708">"הגישה חסומה"</string>
<string name="on" msgid="280241003226755921">"מופעל"</string>
@@ -34,6 +35,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,8 +53,8 @@
<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>
@@ -60,6 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"‏פעולות התקנה/הסרת התקנה אינן נתמכות ב-Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"‏בחירה בהרשאות הגישה שברצונך לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"‏אפליקציית &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; עודכנה. יש לבחור הרשאות גישה לאפליקציה הזו."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ביטול"</string>
@@ -191,20 +192,24 @@
<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_limited_access" msgid="8824410215149764113">"הרשאה לגישה מוגבלת"</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_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_header_with_device_name" msgid="7193042925656173271">"גישה ל<xliff:g id="PERM">%1$s</xliff:g> לאפליקציה הזו ב<xliff:g id="DEVICE_NAME">%2$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_label_v3" msgid="693340578642156657">"ניהול האפליקציה כשהיא לא בשימוש"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"הסרת ההרשאות, מחיקה של הקבצים הזמניים, הפסקה של קבלת ההתראות"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"הסרת ההרשאות, מחיקה של הקבצים הזמניים, הפסקה של קבלת ההתראות והעברת האפליקציה לארכיון"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"כדי להגן על הנתונים שלך, אם האפליקציה הזו לא תהיה בשימוש במשך מספר חודשים, ההרשאות שניתנו לה יוסרו."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"כדי להגן על הנתונים שלך, אם האפליקציה לא תהיה בשימוש במשך מספר חודשים, ההרשאות הבאות יוסרו: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"כדי להגן על הנתונים שלך, הוסרו הרשאות מאפליקציות שלא השתמשת בהן במשך מספר חודשים."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"הרשאה לניהול כל הקבצים"</string>
<string name="ask_header" msgid="2633816846459944376">"יש לשאול בכל פעם"</string>
<string name="denied_header" msgid="903209608358177654">"אין הרשאה"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"‫<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> ב-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"לצפייה באפליקציות נוספות שיכולות לגשת לכל הקבצים"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{יום אחד}one{# ימים}two{יומיים}other{# ימים}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{שעה}one{# שעות}two{שעתיים}other{# שעות}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ברירת המחדל של אפליקציית הארנק"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"אפליקציית ארנק"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"אפליקציות ארנק מאחסנות את כרטיסי האשראי ואת כרטיסי מועדון הלקוחות שלך, את מפתחות הרכב ועוד כדי לעזור בסוגים שונים של עסקאות."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"להגדיר את <xliff:g id="APP_NAME">%1$s</xliff:g> כברירת המחדל של אפליקציית הארנק?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"אין צורך בהרשאות"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ברירת המחדל הנוכחית"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"לא לשאול שוב"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"הגדרה כברירת מחדל"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"עוד ברירות מחדל"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"פתיחת קישורים"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ברירת מחדל לעבודה"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ברירת מחדל עבור המרחב הפרטי"</string>
<string name="default_app_none" msgid="9084592086808194457">"ללא"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ברירת מחדל של המערכת)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"אין אפליקציות"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לתמונות ולמדיה במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לאנשי הקשר במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה למיקום של &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה למיקום של &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"‏לשנות את הרשאת הגישה של האפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; למיקום במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; לאתר מכשירים קרובים, להתחבר אליהם ולזהות את המיקום היחסי שלהם במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"‏לשנות את הרשאת הגישה של האפליקציה <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> למיקום במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה למיקום המשוער של &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה ליומן במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה לשלוח הודעות SMS ולראות אותן במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לתמונות, למדיה ולקבצים במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה למוזיקה ולאודיו במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לתמונות ולסרטונים במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לעוד תמונות וסרטונים במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה להקליט אודיו במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה להקליט אודיו במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"‏לשנות את הרשאת הגישה של האפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; למיקרופון במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לנתונים של הפעילות הגופנית שלך במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה לצלם תמונות ולהקליט סרטונים במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה לצלם תמונות ולהקליט סרטונים במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"‏לשנות את הרשאת הגישה של האפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; למצלמה במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה ליומני השיחות במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה לבצע שיחות טלפון ולנהל אותן במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לנתוני החיישנים שמודדים את הסימנים החיוניים שלך במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאת גישה לנתוני החיישנים שמודדים את הסימנים החיוניים שלך במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"כדי לאפשר לאפליקציה הזו לגשת לנתונים של החיישנים הגופניים כל הזמן, גם כשהיא לא בשימוש, "<annotation id="link">"צריך להיכנס להגדרות."</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_device_aware_sensors" msgid="5542771499929819675">"‏להמשיך לאפשר לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; לגשת לנתוני החיישנים הגופניים במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"‏לתת לאפליקציה &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; הרשאה לשלוח לך התראות במכשיר &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ללא"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"ב-24 השעות\nהאחרונות"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"ב-7 הימים\nהאחרונים"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"‫<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> אחוזים"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"‏האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מוגנת על ידי Android. השימוש של האפליקציה הזו בהרשאות לא מוצג בשורת הסטטוס או במרכז הבקרה להגדרות הפרטיות כי הנתונים שלך מעובדים במכשיר הזה."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"הגישה למצלמה מושבתת"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"הגישה למיקרופון מושבתת"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"הגישה למיקום מושבתת"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"לאפליקציות מידע ובידור"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"לאפליקציות נדרשות"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"האפליקציה הזו נדרשת"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"האפליקציה הזו נדרשת על ידי יצרן הרכב"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"תאריך גישה: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"גישה אתמול: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"תאריך גישה: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ב-<xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"הסיסמה החד-פעמית שלך היא 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"הגדרה מוגבלת"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"מטעמי אבטחה, ההגדרה הזו לא זמינה כרגע."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"בקשת הגישה של האפליקציה ל<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> נדחתה"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏האפליקציה ביקשה הרשאת גישה למידע רגיש שעלולה לסכן את המידע האישי והפיננסי שלך.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>יכול להיות שהאפליקציה לא תעבוד כמו שצריך ללא ההרשאה המוגבלת הזו. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;מידע נוסף על מתן גישה להרשאות&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"בקשת הגישה של האפליקציה לשמש כברירת המחדל של <xliff:g id="ROLE_NAME">%1$s</xliff:g> נדחתה"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"‏האפליקציה ביקשה הרשאת גישה למידע רגיש שעלולה לסכן את המידע האישי והפיננסי שלך.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>יכול להיות שהאפליקציה לא תעבוד כמו שצריך ללא ההרשאות המוגבלות האלו. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;מידע נוסף על מתן גישה להרשאות&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"בקשת הגישה של האפליקציה נדחתה"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"‏מתן גישה להרשאה הזו עלול לסכן את המידע האישי והפיננסי שלך.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>יכול להיות שהאפליקציה לא תעבוד כמו שצריך ללא ההרשאה המוגבלת הזו. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;מידע נוסף על מתן גישה להרשאות&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"מידע נוסף"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"הבנתי"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"בקשת ההרשאה בוטלה"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"האפליקציה הזו מבקשת הרשאות נוספות, אבל אי אפשר לתת הרשאות בזמן פעילות של סטרימינג. צריך לתת את ההרשאה קודם בטלפון."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"לשיחת חירום או שליחת הודעת טקסט במקרה חירום"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"המיקום נשלח לשירותי החירום"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"האפליקציה הזו ניגשה למיקום המכשיר שלך בזמן שיחה או שליחת הודעת טקסט למספר חירום. זה יכול לקרות גם אם לאפליקציה אין הרשאת מיקום או שמיקום המכשיר מושבת. "<a href="https://support.google.com/android/answer/9319337">"מידע נוסף"</a></string>
</resources>
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-v34/strings.xml b/PermissionController/res/values-ja-v34/strings.xml
index 1a27a1df1..bf8c8edf4 100644
--- a/PermissionController/res/values-ja-v34/strings.xml
+++ b/PermissionController/res/values-ja-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"健康に関するデータへのアプリのアクセス権を管理します"</string>
<string name="location_settings" msgid="8863940440881290182">"位置情報へのアクセス"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"アプリとサービスによるアクセス。この設定が OFF の場合でも、緊急通報番号に発信したときは、マイクのデータが共有されることがあります"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"アプリとサービスによるアクセス"</string>
</resources>
diff --git a/PermissionController/res/values-ja-watch/strings.xml b/PermissionController/res/values-ja-watch/strings.xml
index f47df141c..edd0d5c64 100644
--- a/PermissionController/res/values-ja-watch/strings.xml
+++ b/PermissionController/res/values-ja-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"変更不可"</string>
<string name="generic_yes" msgid="2489207724988649846">"はい"</string>
<string name="generic_cancel" msgid="2631708607129269698">"キャンセル"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"常時"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"アプリの使用中"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"常時"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"アプリの使用中"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"常時"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"アプリの使用中"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"常時"</string>
</resources>
diff --git a/PermissionController/res/values-ja/strings.xml b/PermissionController/res/values-ja/strings.xml
index 625668a2c..6adcd0003 100644
--- a/PermissionController/res/values-ja/strings.xml
+++ b/PermissionController/res/values-ja/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"権限"</string>
<string name="cancel" msgid="8943320028373963831">"キャンセル"</string>
<string name="back" msgid="6249950659061523680">"戻る"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"閉じる"</string>
<string name="available" msgid="6007778121920339498">"使用可能"</string>
<string name="blocked" msgid="9195547604866033708">"ブロック中"</string>
<string name="on" msgid="280241003226755921">"ON"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear ではインストールやアンインストールはできません。"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可する権限の選択"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; が更新されました。このアプリに許可する権限を選択してください。"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"キャンセル"</string>
@@ -185,18 +186,20 @@
<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>
<string name="app_permission_button_ask" msgid="3342950658789427">"毎回確認する"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"許可しない"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"制限付きでアクセスを許可する"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> でこのアプリに <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"使用していないアプリを管理する"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"権限と一時ファイルを削除し、通知を停止します"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"権限と一時ファイルを削除し、通知を停止し、アプリをアーカイブします"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"データ保護のため、このアプリが数か月使用されていない場合はアプリの権限が取り消されます。"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"データ保護のため、アプリが数か月使用されていない場合は以下の権限が取り消されます。<xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"データ保護のため、数か月使用していないアプリの権限を削除しました。"</string>
@@ -248,10 +253,11 @@
<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>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>(<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"すべてのファイルにアクセスできるアプリをもっと見る"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 日}other{# 日}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 時間}other{# 時間}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"デフォルトのウォレット アプリ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"ウォレット アプリ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"ウォレット アプリにクレジット カード、ポイントカード、車のキーなどの情報を保存して、さまざまな取引に対応できます。"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> をデフォルトのウォレット アプリに設定しますか?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"権限は必要ありません"</string>
<string name="request_role_current_default" msgid="738722892438247184">"現在のデフォルト"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"次回から表示しない"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"デフォルトに設定"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"その他のデフォルト"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"リンクを開く"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"デフォルトの仕事用アプリ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"プライベート スペースのデフォルト"</string>
<string name="default_app_none" msgid="9084592086808194457">"なし"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(システムのデフォルト)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"アプリなし"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の写真とメディアへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_contacts" msgid="8391550064551053695">"連絡先へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の連絡先へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"このデバイスの位置情報へのアクセスを「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;の位置情報へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;の位置情報へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;の位置情報に対する &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; のアクセス権を変更しますか?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"このアプリは、未使用時も含め、常に位置情報へのアクセスを試みます。"<annotation id="link">"[設定] で許可してください。"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"付近のデバイスの検出、接続、相対位置の特定を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での付近のデバイスの検出、接続、相対位置の特定を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"付近のデバイスの検出、接続、相対位置の特定を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"<annotation id="link">"設定で許可してください。"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> の位置情報へのアクセスを「おおよそ」から「正確」に変更しますか?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;の位置情報に対する &lt;b&gt;<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>&lt;/b&gt; のアクセス権を「おおよそ」から「正確」に変更しますか?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"このデバイスのおおよその位置情報へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;のおおよその位置情報へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内のカレンダーへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS メッセージの送信と表示を「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での 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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の写真、メディア、ファイルへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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;写真、動画、音楽、音声など&lt;/b&gt;へのアクセスを &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の音楽と音声へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の写真と動画へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"このデバイス内の他の写真や動画へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内のその他の写真や動画へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"音声の録音を「&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;」に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での録音を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での録音を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;のマイクに対する &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; のアクセス権を変更しますか?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の身体活動データへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_camera" msgid="5123097035410002594">"写真と動画の撮影を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での写真と動画の撮影を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での写真と動画の撮影を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;のカメラに対する &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; のアクセス権を変更しますか?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内の通話履歴へのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_phone" msgid="1829234136997316752">"電話の発信と管理を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での通話の発信と管理を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; にバイタルサインに関するセンサーデータへのアクセスを許可しますか?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内のバイタルサインに関するセンサーデータへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="permgroupupgraderequestdetail_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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;内のバイタルサインに関するセンサーデータへのアクセスを &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
+ <string name="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_device_aware_sensors" msgid="5542771499929819675">"アプリ使用中に &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; が&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;での通知の送信を &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; に許可しますか?"</string>
<string name="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>
@@ -512,6 +551,9 @@
<string name="privdash_label_none" msgid="5991866260360484858">"なし"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"過去 \n24 時間"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"過去\n7 日間"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for privdash_usage_percent (6893824766124414127) -->
+ <skip />
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> は Android によって保護されています。あなたのデータはこのデバイス上で処理されるため、このアプリの権限の使用状況はステータスバーやプライバシー ダッシュボードには表示されません。"</string>
<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>
@@ -520,6 +562,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"カメラへのアクセスは OFF です"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"マイクへのアクセスが OFF になっています"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"位置情報へのアクセスが OFF になっています"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"対象: インフォテインメント アプリ"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"対象: 必須アプリ"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"このアプリは必要です"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"このアプリは車のメーカーが必要としています"</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>
@@ -583,7 +632,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>
@@ -619,4 +668,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> にアクセス"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"昨日の <xliff:g id="TIME_DATE">%1$s</xliff:g> にアクセス"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> にアクセス"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ワンタイム パスワードは 132435 です"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"制限付き設定"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"セキュリティ保護のため、この設定は現在利用できません。"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"アプリは<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>へのアクセスを拒否されました"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"機密情報に関わる権限へのアクセスをアプリがリクエストしました。この権限へのアクセスは、あなたの個人情報や財務情報を危険にさらす恐れがあります。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>この制限付きの権限がないとアプリは正しく動作しない可能性があります。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;アクセスを許可する方法の詳細&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"アプリはデフォルトの<xliff:g id="ROLE_NAME">%1$s</xliff:g>としてのアクセスを拒否されました"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"機密情報に関わる権限へのアクセスをアプリがリクエストしました。これらの権限へのアクセスは、あなたの個人情報や財務情報を危険にさらす恐れがあります。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>これらの制限付きの権限がないとアプリは正しく動作しない可能性があります。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;アクセスを許可する方法の詳細&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"アプリはアクセスを拒否されました"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"この権限へのアクセスは、あなたの個人情報や財務情報を危険にさらす恐れがあります。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>この制限付きの権限がないとアプリは正しく動作しない可能性があります。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;アクセスを許可する方法の詳細&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"詳細"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"権限のリクエストが抑制されています"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"このアプリは追加の権限を求めていますが、ストリーミング セッションでは権限を付与できません。スマートフォンで先に権限を付与してください。"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"緊急通報(電話またはテキスト メッセージ)の場合"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"緊急サービスへの位置情報の送信"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"このアプリは緊急通報番号に電話またはテキスト メッセージで通報する際にデバイスの位置情報にアクセスしました。このアクセスは、アプリに位置情報の利用許可が付与されていない場合やデバイスの位置情報が OFF の場合も発生することがあります。"<a href="https://support.google.com/android/answer/9319337">"詳細"</a></string>
</resources>
diff --git a/PermissionController/res/values-ka-v34/strings.xml b/PermissionController/res/values-ka-v34/strings.xml
index c3e4c7715..e67e8ffa0 100644
--- a/PermissionController/res/values-ka-v34/strings.xml
+++ b/PermissionController/res/values-ka-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-ka-watch/strings.xml b/PermissionController/res/values-ka-watch/strings.xml
index f3ca03402..357e9cd00 100644
--- a/PermissionController/res/values-ka-watch/strings.xml
+++ b/PermissionController/res/values-ka-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ვერ შეიცვლება"</string>
<string name="generic_yes" msgid="2489207724988649846">"დიახ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"გაუქმება"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Მუდმივად"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"აპის გამოყენებისას"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Მუდმივად"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"აპის გამოყენებისას"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Მუდმივად"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"აპის გამოყენებისას"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Მუდმივად"</string>
</resources>
diff --git a/PermissionController/res/values-ka/strings.xml b/PermissionController/res/values-ka/strings.xml
index 9d20dad4d..12f0f3337 100644
--- a/PermissionController/res/values-ka/strings.xml
+++ b/PermissionController/res/values-ka/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ნებართვები"</string>
<string name="cancel" msgid="8943320028373963831">"გაუქმება"</string>
<string name="back" msgid="6249950659061523680">"უკან"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"დახურვა"</string>
<string name="available" msgid="6007778121920339498">"ხელმისაწვდომი"</string>
<string name="blocked" msgid="9195547604866033708">"დაბლოკილი"</string>
<string name="on" msgid="280241003226755921">"ჩართვა"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"ინსტალაციის/დეინსტალაციის მოქმედებები არ არის მხარდაჭერილი Wear-ზე."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"აირჩიეთ, რაზე ჰქონდეს წვდომა &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-ს"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; განახლდა. აირჩიეთ, რაზე ჰქონდეს წვდომა ამ აპს."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"გაუქმება"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"შეზღუდული წვდომის დაშვება"</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_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_with_device_name" msgid="7193042925656173271">"(<xliff:g id="PERM">%1$s</xliff:g>) წვდომა ამ აპისთვის <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"აპის მართვა გამოუყენებლობის შემთხვევაში"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ნებართვების ამოშლა, დროებითი ფაილების წაშლა და შეტყობინებების გამორთვა"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ნებართვების ამოშლა, დროებითი ფაილების წაშლა, შეტყობინებების გამორთვა და აპის დაარქივება"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"თქვენი მონაცემების დასაცავად ნებართვები ამოიშლება ამ აპიდან, თუ ის რამდენიმე თვის განმავლობაში არ გამოგიყენებიათ."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"თქვენი მონაცემების დაცვის მიზნით, თუ აპს რამდენიმე თვის განმავლობაში არ გამოიყენებთ, ამოიშლება შემდეგი ნებართვები: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"თქვენი მონაცემების დასაცავად ნებართვები ამოიშალა აპებიდან, რომლებიც რამდენიმე თვის განმავლობაში არ გამოგიყენებიათ."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"დაშვებულია ყველა ფაილის მართვა"</string>
<string name="ask_header" msgid="2633816846459944376">"ყოველთვის მკითხე"</string>
<string name="denied_header" msgid="903209608358177654">"არ არის დაშვებული"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"სხვა აპების ნახვა, რომლებსაც ყველა ფაილზე წვდომა შეუძლია"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 დღე}other{# დღე}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# საათი}other{# საათი}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ნაგულისხმევი საფულის აპი"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"საფულის აპი"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"საფულის აპებს შეუძლია საკრედიტო და ერთგული კლიენტის ბარათების, მანქანის გასაღებების და სხვა ნივთების შენახვა, რაც დაგეხმარებათ სხვადასხვა სახის ტრანზაქციებში."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"გსურთ დააყენოთ <xliff:g id="APP_NAME">%1$s</xliff:g> თქვენს ნაგულისხმევ საფულის აპად?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ნებართვები არ არის საჭირო"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ამჟამინდელი ნაგულისხმევი"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"აღარ მკითხოთ"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ნაგულისხმ. დაყენება"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"მეტი ნაგულისხმევი"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ბმულების გახსნა"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ნაგულისხმევი სამსახურისთვის"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"კერძო სივრცისთვის ნაგულისხმევი აპები"</string>
<string name="default_app_none" msgid="9084592086808194457">"არცერთი"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(სისტემის ნაგულისხმევი)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"აპები არ არის"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა ფოტოებსა და მედიაზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა თქვენს კონტაქტებზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_location" msgid="6075412127429878638">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_location" msgid="1264484517831380016">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპისთვის მდებარეობაზე წვდომის შეცვლა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს ნებართვა იპოვოს ახლომახლო მოწყობილობები, დაუკავშირდეს მათ და დაადგინოს მათი შედარებითი პოზიცია &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"გსურთ <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> აპის დაშვებული წვდომის შეცვლა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_calendar" msgid="7161929851377463612">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა თქვენს კალენდარზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&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_device_aware_sms" msgid="6639977653040502291">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს დაშვება გააგზავნოს და ნახოს SMS შეტყობინებები &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა თქვენს ფოტოებზე, მედიასა და ფაილებზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა მუსიკასა და აუდიოზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა ფოტოებსა და ვიდეოებზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა მეტ ფოტოსა და ვიდეოზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს აუდიოს ჩაწერის ნებართვა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს აუდიოს ჩაწერის ნებართვა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპისთვის მიკროფონის წვდომის შეცვლა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;-ზე?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"ეს აპი აუდიოს ჩაწერს ნებისმიერ დროს, მაშინაც კი, როცა აპს არ იყენებთ. "<annotation id="link">"Allow in settings."</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_device_aware_activityRecognition" msgid="1243869530588745374">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს თქვენს ფიზიკურ აქტივობაზე წვდომა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს სურათების გადაღებისა და ვიდეოს ჩაწერის ნებართვა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს სურათების გადაღებისა და ვიდეოს ჩაწერის ნებართვა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპისთვის კამერის წვდომის შეცვლა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს თქვენი ტელეფონის ზარების ჟურნალებზე წვდომა &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს დაშვება ზარების განხორციელებასა და მართვაზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა თქვენი სასიცოცხლო მაჩვენებლების სენსორის მონაცემებზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს წვდომა თქვენი სასიცოცხლო მაჩვენებლების სენსორის მონაცემებზე &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;-ზე?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ამ აპისთვის სხეულის სენსორების მონაცემებზე მუდმივი წვდომის მისანიჭებლად (მაშინაც კი, როცა აპს არ იყენებთ), "<annotation id="link">"გადადით პარამეტრებზე."</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_device_aware_sensors" msgid="5542771499929819675">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპისთვის სხეულის სენსორის მონაცემებზე წვდომის ნებართვის შენარჩუნება აპის გამოყენებისას &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"გსურთ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; აპს ჰქონდეს დაშვება გამოგიგზავნოთ შეტყობინებები &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"არცერთი"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"გასული\n24 საათი"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">\n"ბოლო 7 დღე"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> პროცენტი"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს იცავს Android. ვინაიდან თქვენი მონაცემები მუშავდება ამ მოწყობილობაზე, ამ აპის ნებართვის გამოყენება არ აისახება სტატუსის ზოლში ან კონფიდენციალურობის საინფორმაციო დაფაზე."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"კამერის წვდომა გამორთულია"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"მიკროფონზე წვდომა გამორთულია"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"მდებარეობაზე წვდომა გამორთულია"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"საინფორმაციო-გასართობი აპებისათვის"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"სავალდებულო აპებისათვის"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ეს აპი სავალდებულოა"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ეს აპი მოთხოვნილია თქვენი მანქანის მწარმოებლის მიერ"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"წვდომა განხორციელდა: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"წვდომა განხორციელდა გუშინ: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"წვდომა განხორციელდა: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"თქვენი ერთჯერადი პაროლია: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"შეზღუდული პარამეტრი"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"თქვენივე უსაფრთხოებისთვის ეს პარამეტრი ამჟამად მიუწვდომელია."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"აპისთვის უარყოფილია ნებართვაზე წვდომა: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"აპმა სენსიტიურ ინფორმაციაზე წვდომა მოითხოვა, რაც თქვენს პირად და ფინანსურ ინფორმაციას საფრთხის ქვეშ აყენებს.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>შესაძლოა აპმა ამ შეზღუდული ნებართვის გარეშე სათანადოდ ვერ იმუშაოს. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;შეიტყვეთ მეტი ნებართვის დაშვების შესახებ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"აპისთვის ნაგულისხმევ როლზე (<xliff:g id="ROLE_NAME">%1$s</xliff:g>) წვდომა უარყოფილია"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"აპმა სენსიტიურ ინფორმაციაზე წვდომა მოითხოვა, რაც თქვენს პირად და ფინანსურ ინფორმაციას საფრთხის ქვეშ აყენებს.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>შესაძლოა აპმა ამ შეზღუდული ნებართვის გარეშე სათანადოდ ვერ იმუშაოს. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;შეიტყვეთ მეტი ნებართვის დაშვების შესახებ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"აპისთვის წვდომა უარყოფილია"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ამ ნებართვაზე წვდომა თქვენს პირად და ფინანსურ ინფორმაციას საფრთხის ქვეშ აყენებს.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>შესაძლოა აპმა ამ შეზღუდული ნებართვის გარეშე სათანადოდ ვერ იმუშაოს. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;შეიტყვეთ მეტი ნებართვის დაშვების შესახებ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"შეიტყვეთ მეტი"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"კარგი"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ნებართვის მოთხოვნა შეჩერებულია"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ეს აპი დამატებით ნებართვებს მოითხოვს, მაგრამ ნებართვების მიცემა შეუძლებელია სტრიმინგის სესიაში. თავდაპირველად მიანიჭეთ ნებართვა ტელეფონზე."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"გადაუდებელი ზარის ან ტექსტური შეტყობინებისთვის"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"გადაუდებელი დახმარების სამსახურებთან მდებარეობის გაგზავნა"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ამ აპმა წვდომა მიიღო თქვენი მოწყობილობის მდებარეობაზე ზარის ან ტექსტური შეტყობინების გაგზავნისას გადაუდებელი დახმარების სამსახურის ნომერზე. ეს შეიძლება მოხდეს მაშინაც კი, როდესაც აპს არ აქვს მდებარეობის ნებართვა ან მოწყობილობის მდებარეობა გამორთულია. "<a href="https://support.google.com/android/answer/9319337">"შეიტყვეთ მეტი"</a></string>
</resources>
diff --git a/PermissionController/res/values-kk-v34/strings.xml b/PermissionController/res/values-kk-v34/strings.xml
index 002624b5a..0e992048f 100644
--- a/PermissionController/res/values-kk-v34/strings.xml
+++ b/PermissionController/res/values-kk-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-kk-watch/strings.xml b/PermissionController/res/values-kk-watch/strings.xml
index 1adaab343..8cbb401c4 100644
--- a/PermissionController/res/values-kk-watch/strings.xml
+++ b/PermissionController/res/values-kk-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Өзгерту мүмкін емес"</string>
<string name="generic_yes" msgid="2489207724988649846">"Иә"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Бас тарту"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Әрдайым"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Қолданбаны пайдаланғанда"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Әрдайым"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Қолданбаны пайдаланғанда"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Әрдайым"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Қолданбаны пайдаланғанда"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Әрдайым"</string>
</resources>
diff --git a/PermissionController/res/values-kk/strings.xml b/PermissionController/res/values-kk/strings.xml
index 72320b1d0..99742080b 100644
--- a/PermissionController/res/values-kk/strings.xml
+++ b/PermissionController/res/values-kk/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"рұқсаттар"</string>
<string name="cancel" msgid="8943320028373963831">"Бас тарту"</string>
<string name="back" msgid="6249950659061523680">"Артқа"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Жабу"</string>
<string name="available" msgid="6007778121920339498">"Қолжетімді"</string>
<string name="blocked" msgid="9195547604866033708">"Бөгелген"</string>
<string name="on" msgid="280241003226755921">"Қосулы"</string>
@@ -29,11 +30,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>
@@ -49,7 +51,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 +62,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 +73,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,14 +111,12 @@
<!-- 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>
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear жүйесінде \"Орнату\"/\"Жою\" әрекеттері қолданылмайды."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы қайда кіре алатынын таңдаңыз"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы жаңартылды. Бұл қолданбаның қайда кіре алатынын таңдаңыз."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Бас тарту"</string>
@@ -186,17 +187,19 @@
<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>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Рұқсат бермеу"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Шектеулі рұқсат беру"</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_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_with_device_name" msgid="7193042925656173271">"Құрылғыдағы (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) осы қолданбаның рұқсаты: <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Пайдаланылмайтын қолданбалардың жұмысын кідірту"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Рұқсаттарды өшіру, уақытша файлдарды жою және хабарландыруларды тоқтату"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Рұқсаттарды өшіру, уақытша файлдарды жою, хабарландыруларды тоқтату және қолданбаны мұрағаттау"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Бұл қолданба бірнеше ай бойы пайдаланылмаса, деректеріңізді қорғау үшін оған берілген рұқсаттар өшіріледі."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Қолданба бірнеше ай бойы пайдаланылмаса, деректеріңізді қорғау үшін мына рұқсаттар өшіріледі: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Бірнеше ай бойы пайдаланылмаған қолданбаларға берілген рұқсаттар деректеріңізді қорғау үшін өшірілді."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Барлық файлдарды басқара алатын қолданбалар"</string>
<string name="ask_header" msgid="2633816846459944376">"Әрдайым сұрау"</string>
<string name="denied_header" msgid="903209608358177654">"Рұқсат берілмегендер"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Барлық файлды пайдалана алатын тағы басқа қолданбаларды көру"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 күн}other{# күн}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# сағат}other{# сағат}}"</string>
@@ -386,7 +392,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Әдепкі әмиян қолданбасы"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Әмиян қолданбасы"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Әмиян қолданбаларында несиелік және тұрақты клиент карталарын, көлік кілттерін, сондай-ақ басқа да транзакция түрлері үшін қолайлы заттарды сақтауға болады."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын әдепкі әмиян қолданбасы ретінде орнату керек пе?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Рұқсаттар қажет емес."</string>
<string name="request_role_current_default" msgid="738722892438247184">"Ағымдағы әдепкі қолданба"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Қайта сұралмасын"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Әдепкі етіп орнату"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Басқа да әдепкі қолданбалар"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Сілтемелер ашу"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Жұмыс үшін әдепкі қолданба"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Құпия кеңістікке арналған әдепкі қолданбалар"</string>
<string name="default_app_none" msgid="9084592086808194457">"Жоқ"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(жүйенің әдепкі қолданбасы)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Қолданба жоқ"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы үшін &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> қолданбасының &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы үшін &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы үшін &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; құрылғысында тіршілік көрсеткіштеріңіз туралы датчик деректерін пайдалану рұқсаты берілсін бе?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Бұл қолданбаға кез келген уақытта (ол пайдаланылмаған кезде де) дене датчигінен алынған деректі пайдалануға рұқсат беру үшін "<annotation id="link">"параметрлерге өтіңіз."</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_device_aware_sensors" msgid="5542771499929819675">"Жұмыс кезінде &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасы &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; қолданбасына &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Жоқ"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Соңғы\n24 сағатта"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Соңғы\n7 күн"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> пайыз"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын Android қорғайды. Деректеріңіз осы құрылғыда өңделетіндіктен, бұл қолданба рұқсатының пайдаланылуы күй жолағында немесе құпиялық тақтасында көрсетілмейді."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Камераны пайдалану рұқсаты өшірулі"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Микрофон рұқсаты өшірулі"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Локация рұқсаты өшірулі"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Ақпараттық-сауықтық қолданбалар"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Қажетті қолданбалар үшін"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Осы қолданба қажет"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Осы қолданба көлік өндірушісі үшін қажет."</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Пайдаланылған уақыты: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Кеше пайдаланылған уақыты: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Пайдаланылған уақыты: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Бір реттік құпия сөзіңіз: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Шектелген параметр"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Қауіпсіздік мақсатында бұл параметрді қазір пайдалану мүмкін емес."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Қолданбаға <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> рұқсаты берілмеді"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Қолданба жеке және қаржылық ақпаратыңызға қауіп төндіруі мүмкін құпия ақпарат рұқсатын сұрады.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Қолданба бұл шектеулі рұқсатсыз дұрыс жұмыс істемеуі мүмкін. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Рұқсат беру туралы ақпарат&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Қолданбаға әдепкі <xliff:g id="ROLE_NAME">%1$s</xliff:g> болу рұқсаты берілмеді"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Қолданба жеке және қаржылық ақпаратыңызға қауіп төндіруі мүмкін құпия ақпарат рұқсаттарын сұрады.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Қолданба бұл шектеулі рұқсаттарсыз дұрыс жұмыс істемеуі мүмкін. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Рұқсат беру туралы ақпарат&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Қолданбаға рұқсат берілмеді"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Бұл рұқсатты берсеңіз, жеке және қаржылық ақпаратыңызға қауіп төнуі мүмкін.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Қолданба бұл шектеулі рұқсатсыз дұрыс жұмыс істемеуі мүмкін. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Рұқсат беру туралы ақпарат&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Толық ақпарат"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Жарайды"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Рұқсат сұрауы блокталды"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Бұл қолданба қосымша рұқсаттар сұрап жатыр, бірақ трансляция жүріп жатқанда рұқсат беру мүмкін емес. Алдымен телефонда рұқсат беріңіз."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Құтқару қызметіне қоңырау шалуға немесе мәтіндік хабар жіберуге арналған"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Локация құтқару қызметтеріне жіберілді"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Бұл қолданба құтқару қызметінің нөміріне қоңырау шалу немесе мәтіндік хабар жіберу кезінде құрылғының локациясын пайдаланды. Локацияны пайдалану рұқсаты болмаған немесе құрылғы локациясы өшірулі болған жағдайда да, қолданба локацияны пайдалануы мүмкін. "<a href="https://support.google.com/android/answer/9319337">"Толық ақпарат"</a></string>
</resources>
diff --git a/PermissionController/res/values-km-v34/strings.xml b/PermissionController/res/values-km-v34/strings.xml
index ccf92d186..8dbc027cf 100644
--- a/PermissionController/res/values-km-v34/strings.xml
+++ b/PermissionController/res/values-km-v34/strings.xml
@@ -17,11 +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="location_settings" msgid="8863940440881290182">"ការចូលប្រើប្រាស់ទីតាំង"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"សម្រាប់កម្មវិធី និងសេវាកម្ម។ ប្រសិនបើការកំណត់នេះ​ត្រូវ​បាន​បិទ ទិន្នន័យមីក្រូហ្វូននៅតែអាចត្រូវបានចែករំលែកដដែល នៅពេលអ្នកហៅទៅលេខសង្គ្រោះបន្ទាន់"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"សម្រាប់កម្មវិធី និងសេវាកម្ម"</string>
</resources>
diff --git a/PermissionController/res/values-km-watch/strings.xml b/PermissionController/res/values-km-watch/strings.xml
index de12569e2..08504f8f1 100644
--- a/PermissionController/res/values-km-watch/strings.xml
+++ b/PermissionController/res/values-km-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"មិនអាចប្តូរបានទេ"</string>
<string name="generic_yes" msgid="2489207724988649846">"បាទ/ចាស"</string>
<string name="generic_cancel" msgid="2631708607129269698">"បោះបង់"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"គ្រប់ពេល"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ពេលកំពុងប្រើកម្មវិធី"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"គ្រប់ពេល"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ពេលកំពុងប្រើកម្មវិធី"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"គ្រប់ពេល"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ពេលកំពុងប្រើកម្មវិធី"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"គ្រប់ពេល"</string>
</resources>
diff --git a/PermissionController/res/values-km/strings.xml b/PermissionController/res/values-km/strings.xml
index a50189176..74fdd859a 100644
--- a/PermissionController/res/values-km/strings.xml
+++ b/PermissionController/res/values-km/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ការអនុញ្ញាត"</string>
<string name="cancel" msgid="8943320028373963831">"បោះបង់"</string>
<string name="back" msgid="6249950659061523680">"ថយក្រោយ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"បិទ"</string>
<string name="available" msgid="6007778121920339498">"អាចចូលប្រើបាន"</string>
<string name="blocked" msgid="9195547604866033708">"បាន​ទប់ស្កាត់"</string>
<string name="on" msgid="280241003226755921">"បើក"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"សកម្មភាព​ដំឡើង/លុបចេញមិនអាចប្រើ​នៅលើ Wear បានទេ។"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"ជ្រើសរើស​អ្វីដែលត្រូវ​អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ត្រូវបាន​ដំឡើងជំនាន់។ ជ្រើសរើស​អ្វីដែលត្រូវ​អនុញ្ញាតឱ្យ​កម្មវិធីនេះ​ចូលប្រើ។"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"បោះបង់"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"អនុញ្ញាតឱ្យចូលប្រើដោយមានកម្រិត"</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_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_with_device_name" msgid="7193042925656173271">"សិទ្ធិចូលប្រើ <xliff:g id="PERM">%1$s</xliff:g> សម្រាប់កម្មវិធីនេះនៅលើ <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"គ្រប់គ្រងកម្មវិធី ប្រសិនបើមិនបានប្រើ"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ដកការអនុញ្ញាតចេញ លុបឯកសារបណ្ដោះអាសន្ន និងបញ្ឈប់ការជូនដំណឹង"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ដកការអនុញ្ញាតចេញ លុបឯកសារបណ្ដោះអាសន្ន បញ្ឈប់ការជូនដំណឹង និងទុកកម្មវិធីក្នុងបណ្ណសារ"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ដើម្បីការពារ​ទិន្នន័យរបស់អ្នក ការអនុញ្ញាត​សម្រាប់កម្មវិធីនេះ​នឹងត្រូវ​ដកចេញ ប្រសិនបើ​មិនប្រើប្រាស់​កម្មវិធីនេះ​រយៈពេល​ពីរបីខែ។"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"ដើម្បីការពារ​ទិន្នន័យរបស់អ្នក ការអនុញ្ញាត​ខាងក្រោម​នឹងត្រូវដកចេញ ប្រសិនបើ​មិនប្រើប្រាស់​កម្មវិធីនេះ​រយៈពេល​ពីរបីខែ៖ <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ដើម្បីការពារ​ទិន្នន័យរបស់អ្នក ការអនុញ្ញាត​ត្រូវបាន​ដកចេញ​ពីកម្មវិធី​ដែល​អ្នកមិនបាន​ប្រើប្រាស់​រយៈពេល​ពីរបីខែ។"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"បានអនុញ្ញាតឱ្យគ្រប់គ្រងឯកសារទាំងអស់"</string>
<string name="ask_header" msgid="2633816846459944376">"សួរគ្រប់ពេល"</string>
<string name="denied_header" msgid="903209608358177654">"មិន​បានអនុញ្ញាត​"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> នៅលើ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"មើលកម្មវិធីច្រើនទៀតដែលអាចចូលប្រើឯកសារទាំងអស់បាន"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ថ្ងៃ}other{# ថ្ងៃ}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ម៉ោង}other{# ម៉ោង}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"កម្មវិធីកាបូបលំនាំដើម"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"កម្មវិធីកាបូប"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"កម្មវិធីកាបូបអាចរក្សាទុកកាតឥណទាននិងកាតសមាជិក សោរថយន្ត និងអ្វីៗផ្សេងទៀតរបស់អ្នក ដើម្បីជួយពាក់ព័ន្ធនឹងទម្រង់ផ្សេងៗរបស់ប្រតិបត្តិការ។"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"កំណត់ <xliff:g id="APP_NAME">%1$s</xliff:g> ជាកម្មវិធីកាបូបលំនាំដើមរបស់អ្នកឬ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"មិនត្រូវការ​ការអនុញ្ញាត​ទេ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"លំនាំដើម​បច្ចុប្បន្ន"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"កុំសួរទៀត"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"កំណត់ជាលំនាំដើម"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"លំនាំដើម​ច្រើនទៀត"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ការបើកតំណ"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"លំនាំដើម​សម្រាប់ការងារ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"លំនាំដើមសម្រាប់លំហឯកជន"</string>
<string name="default_app_none" msgid="9084592086808194457">"គ្មាន"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(លំនាំដើមប្រព័ន្ធ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"គ្មានកម្មវិធី​ទេ​"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើរូបថត និងមេឌៀនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទំនាក់ទំនងរបស់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទីតាំង&lt;b&gt;របស់ <xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទីតាំង&lt;b&gt;របស់ <xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"ប្ដូរសិទ្ធិចូលប្រើទីតាំងសម្រាប់ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; នៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ស្វែងរក ភ្ជាប់ទៅ និងកំណត់ទីតាំងដែលពាក់ព័ន្ធនៃឧបករណ៍នៅជិតនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"ប្ដូរសិទ្ធិចូលប្រើទីតាំងរបស់ <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> នៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទីតាំងប្រហាក់ប្រហែលរបស់ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើប្រតិទិនរបស់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ផ្ញើ និងមើលសារ SMS នៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើរូបថត មេឌៀ និងឯកសារនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើតន្ត្រី និងសំឡេងនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើរូបថត និងវីដេអូនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើរូបថត និងវីដេអូច្រើនទៀតនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតសំឡេងនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតសំឡេងនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"ប្ដូរសិទ្ធិចូលប្រើមីក្រូហ្វូនសម្រាប់ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; នៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើសកម្មភាព​រាងកាយរបស់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតរូបភាព និងថតវីដេអូនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ថតរូបភាព និងថតវីដេអូនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"ប្ដូរសិទ្ធិចូលប្រើកាមេរ៉ាសម្រាប់ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; នៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើកំណត់​ហេតុ​ហៅ​ទូរសព្ទរបស់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ធ្វើការហៅទូរសព្ទ និងគ្រប់គ្រងការហៅទូរសព្ទនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទិន្នន័យសេនស័រអំពីសញ្ញាសរីរាង្គសំខាន់ៗរបស់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទិន្នន័យសេនស័រអំពីសញ្ញាសរីរាង្គសំខាន់ៗរបស់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ឬ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ដើម្បីអនុញ្ញាតឱ្យកម្មវិធីនេះចូលប្រើទិន្នន័យឧបករណ៍ចាប់សញ្ញារាងកាយគ្រប់ពេល ទោះបីជានៅពេលអ្នកមិនកំពុងប្រើកម្មវិធីនេះក៏ដោយ "<annotation id="link">"សូមចូលទៅកាន់ការកំណត់។"</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_device_aware_sensors" msgid="5542771499929819675">"បន្តអនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ចូលប្រើទិន្នន័យសេនស័ររាងកាយនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"អនុញ្ញាតឱ្យ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ផ្ញើការជូនដំណឹងដល់អ្នកនៅលើ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"គ្មាន"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 ម៉ោង\nចុងក្រោយ"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 ថ្ងៃ​\nចុង​ក្រោយ"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ភាគរយ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ត្រូវបានការពារ​ដោយ Android។ ដោយសារ​ទិន្នន័យរបស់អ្នក​ត្រូវបានដំណើរការ​នៅលើឧបករណ៍នេះ ការប្រើប្រាស់​ការអនុញ្ញាត​របស់កម្មវិធីនេះ​មិនបង្ហាញនៅលើ​របារស្ថានភាព ឬផ្ទាំងគ្រប់គ្រង​ឯកជនភាព​របស់អ្នកទេ។"</string>
<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>
@@ -520,7 +560,14 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"សិទ្ធិចូលប្រើកាមេរ៉ាត្រូវបានបិទ"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"សិទ្ធិចូលប្រើមីក្រូហ្វូនត្រូវបានបិទ"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"សិទ្ធិចូលប្រើទីតាំងត្រូវបានបិទ"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"សម្រាប់កម្មវិធីព័ត៌មាននិងកម្សាន្ត"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"សម្រាប់កម្មវិធីដែលតម្រូវឱ្យមាន"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"តម្រូវឱ្យមានកម្មវិធីនេះ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ក្រុមហ៊ុនផលិតរថយន្តរបស់អ្នកតម្រូវឱ្យមានកម្មវិធីនេះ"</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 +578,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"បានចូលប្រើនៅម៉ោង <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"បានចូលប្រើម្សិលមិញនៅម៉ោង <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"បានចូលប្រើនៅថ្ងៃទី <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ពាក្យ​សម្ងាត់​ប្រើបាន​តែ​ម្ដងរបស់អ្នកគឺ 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ការកំណត់​ដែលបានដាក់កំហិត"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ដើម្បីសុវត្ថិភាព​របស់អ្នក បច្ចុប្បន្នមិនអាចប្រើការកំណត់នេះបានទេ។"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើ<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"កម្មវិធីបានស្នើសុំសិទ្ធិចូលប្រើការអនុញ្ញាត​ដែលមានលក្ខណៈរសើប ដែលអាចធ្វើឱ្យព័ត៌មានហិរញ្ញវត្ថុ និងព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកប្រឈមនឹងហានិភ័យ។<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>កម្មវិធីអាចនឹងមិនដំណើរការបានត្រឹមត្រូវទេ ប្រសិនបើគ្មានការអនុញ្ញាតដែលមានការរឹតបន្តឹងនេះ។ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ស្វែងយល់អំពីរបៀបផ្ដល់សិទ្ធិចូលប្រើ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើជា<xliff:g id="ROLE_NAME">%1$s</xliff:g>លំនាំដើម"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"កម្មវិធីបានស្នើសុំសិទ្ធិចូលប្រើការអនុញ្ញាត​ដែលមានលក្ខណៈរសើប ដែលអាចធ្វើឱ្យព័ត៌មានហិរញ្ញវត្ថុ និងព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នកប្រឈមនឹងហានិភ័យ។<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>កម្មវិធីអាចនឹងមិនដំណើរការបានត្រឹមត្រូវទេ ប្រសិនបើគ្មានការអនុញ្ញាតដែលមានការរឹតបន្តឹងទាំងនេះ។ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ស្វែងយល់អំពីរបៀបផ្ដល់សិទ្ធិចូលប្រើ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"កម្មវិធីត្រូវបានបដិសេធមិនឱ្យចូលប្រើ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"សិទ្ធិចូលប្រើការអនុញ្ញាតនេះអាចនាំឱ្យព័ត៌មានផ្ទាល់ខ្លួន និងព័ត៌មានហិរញ្ញវត្ថុរបស់អ្នកប្រឈមនឹងហានិភ័យ។<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>កម្មវិធីអាចនឹងមិនដំណើរការបានត្រឹមត្រូវទេ ប្រសិនបើគ្មានការអនុញ្ញាតដែលមានការរឹតបន្តឹងនេះ។ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ស្វែងយល់អំពីរបៀបផ្ដល់សិទ្ធិចូលប្រើ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ស្វែងយល់បន្ថែម"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"យល់ព្រម"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"បានទប់ស្កាត់សំណើសុំការអនុញ្ញាត"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"កម្មវិធីនេះកំពុងស្នើសុំការអនុញ្ញាតបន្ថែម ប៉ុន្តែមិនអាចផ្តល់ការអនុញ្ញាតក្នុងវគ្គផ្សាយបានទេ។ ផ្តល់ការអនុញ្ញាតនៅលើទូរសព្ទរបស់អ្នកជាមុនសិន។"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"សម្រាប់ការហៅ ឬផ្ញើសារជាអក្សរទៅលេខសង្គ្រោះបន្ទាន់"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"បានផ្ញើទីតាំង​ទៅសេវាសង្គ្រោះបន្ទាន់"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"កម្មវិធីនេះបានចូលប្រើទីតាំងឧបករណ៍របស់អ្នកក្នុងអំឡុងពេលហៅទូរសព្ទ ឬផ្ញើសារជាអក្សរទៅកាន់លេខសង្គ្រោះបន្ទាន់។ ករណីនេះអាចកើតឡើង ទោះបីជាកម្មវិធីនេះមិនមានការអនុញ្ញាតទីតាំង ឬបិទទីតាំងឧបករណ៍ក៏ដោយ។ "<a href="https://support.google.com/android/answer/9319337">"ស្វែងយល់បន្ថែម"</a></string>
</resources>
diff --git a/PermissionController/res/values-kn-v34/strings.xml b/PermissionController/res/values-kn-v34/strings.xml
index 34a939459..0e0a442d9 100644
--- a/PermissionController/res/values-kn-v34/strings.xml
+++ b/PermissionController/res/values-kn-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-kn-watch/strings.xml b/PermissionController/res/values-kn-watch/strings.xml
index a05c39b72..1aae70857 100644
--- a/PermissionController/res/values-kn-watch/strings.xml
+++ b/PermissionController/res/values-kn-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ಬದಲಿಸಲಾಗುವುದಿಲ್ಲ"</string>
<string name="generic_yes" msgid="2489207724988649846">"ಹೌದು"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ರದ್ದುಮಾಡಿ"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ಯಾವಾಗಲೂ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ಆ್ಯಪ್ ಬಳಸುವಾಗ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ಯಾವಾಗಲೂ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ಆ್ಯಪ್ ಬಳಸುವಾಗ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ಯಾವಾಗಲೂ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ಆ್ಯಪ್ ಬಳಸುವಾಗ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ಯಾವಾಗಲೂ"</string>
</resources>
diff --git a/PermissionController/res/values-kn/strings.xml b/PermissionController/res/values-kn/strings.xml
index 62868d4f9..eb34f0cb7 100644
--- a/PermissionController/res/values-kn/strings.xml
+++ b/PermissionController/res/values-kn/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ಅನುಮತಿಗಳು"</string>
<string name="cancel" msgid="8943320028373963831">"ರದ್ದುಮಾಡಿ"</string>
<string name="back" msgid="6249950659061523680">"ಹಿಂದಕ್ಕೆ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"ಮುಚ್ಚಿರಿ"</string>
<string name="available" msgid="6007778121920339498">"ಲಭ್ಯವಿದೆ"</string>
<string name="blocked" msgid="9195547604866033708">"ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
<string name="on" msgid="280241003226755921">"ಆನ್"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear ನಲ್ಲಿ ಇನ್‌ಸ್ಟಾಲ್/ಅನ್ಇನ್‌ಸ್ಟಾಲ್ ಕ್ರಿಯೆಗಳು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್‌ಗೆ ಪ್ರವೇಶಿಸಲು ಯಾವುದನ್ನು ಅನುಮತಿಸಬೇಕು ಎಂಬುದನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾಗಿದೆ. ಈ ಆ್ಯಪ್‌ಗೆ ಪ್ರವೇಶಿಸಲು ಯಾವುದನ್ನು ಅನುಮತಿಸಬೇಕು ಎಂಬುದನ್ನು ಆಯ್ಕೆಮಾಡಿ."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ರದ್ದುಮಾಡಿ"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"ಸೀಮಿತ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಅನುಮತಿಸಿ"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ನಲ್ಲಿ ಈ ಆ್ಯಪ್‌ಗಾಗಿ <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"ಬಳಸದಿದ್ದರೆ ಆ್ಯಪ್ ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ಅನುಮತಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ, ತಾತ್ಕಾಲಿಕ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸಿ ಹಾಗೂ ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿಲ್ಲಿಸಿ"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ಅನುಮತಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ, ತಾತ್ಕಾಲಿಕ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸಿ, ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ನಿಲ್ಲಿಸಿ ಮತ್ತು ಆ್ಯಪ್ ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಿ"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ಆ್ಯಪ್ ಅನ್ನು ಕೆಲವು ತಿಂಗಳುಗಳ ಕಾಲ ಬಳಸದಿದ್ದರೆ, ನಿಮ್ಮ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ಈ ಆ್ಯಪ್‌ನ ಅನುಮತಿಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗುವುದು."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"ಆ್ಯಪ್ ಅನ್ನು ಕೆಲವು ತಿಂಗಳುಗಳ ಕಾಲ ಬಳಸದಿದ್ದರೆ, ನಿಮ್ಮ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು, ಈ ಕೆಳಗಿನ ಅನುಮತಿಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗುವುದು: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ನಿಮ್ಮ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು, ಕೆಲವು ತಿಂಗಳುಗಳಿಂದ ನೀವು ಬಳಸದಿರುವ ಆ್ಯಪ್‌ಗಳಿಂದ ಅನುಮತಿಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ಎಲ್ಲಾ ಫೈಲ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಅನುಮತಿಸಲಾಗಿದೆ"</string>
<string name="ask_header" msgid="2633816846459944376">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
<string name="denied_header" msgid="903209608358177654">"ಅನುಮತಿಸಿಲ್ಲದಿರುವುದು"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ನಲ್ಲಿನ <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ಎಲ್ಲಾ ಫೈಲ್‌ಗಳನ್ನು ಪ್ರವೇಶಿಸಬಹುದಾದ ಇನ್ನಷ್ಟು ಆ್ಯಪ್‌ಗಳನ್ನು ನೋಡಿ"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ದಿನ}one{# ದಿನಗಳು}other{# ದಿನಗಳು}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ಗಂಟೆ}one{# ಗಂಟೆಗಳು}other{# ಗಂಟೆಗಳು}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ಡೀಫಾಲ್ಟ್ ವಾಲೆಟ್ ಆ್ಯಪ್"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet ಆ್ಯಪ್"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"ವಾಲೆಟ್ ಆ್ಯಪ್‌ಗಳು ನಿಮ್ಮ ಕ್ರೆಡಿಟ್ ಮತ್ತು ಲಾಯಲ್ಟಿ ಕಾರ್ಡ್‌ಗಳು, ಕಾರ್ ಕೀಗಳು ಮತ್ತು ವಿವಿಧ ವಹಿವಾಟುಗಳಿಗೆ ಸಹಾಯ ಮಾಡುವ ಇತರ ವಿಷಯಗಳನ್ನು ಸಂಗ್ರಹಿಸಬಹುದು."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಅನ್ನು ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ವಾಲೆಟ್ ಆ್ಯಪ್ ಆಗಿ ಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ಯಾವುದೇ ಅನುಮತಿಗಳ ಅಗತ್ಯವಿಲ್ಲ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ಪ್ರಸ್ತುತ ಡೀಫಾಲ್ಟ್"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ಮತ್ತೆ ಕೇಳಬೇಡ"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ಡೀಫಾಲ್ಟ್ ಆಗಿ ಸೆಟ್ ಮಾಡಿ"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"ಇನ್ನಷ್ಟು ಡೀಫಾಲ್ಟ್‌ಗಳು"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ಲಿಂಕ್‍‍ಗಳನ್ನು ತೆರೆಯುವುದು"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ಕೆಲಸದ ಕುರಿತಾದ ಡೀಫಾಲ್ಟ್ ಆ್ಯಪ್"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್‌ನ ಡೀಫಾಲ್ಟ್"</string>
<string name="default_app_none" msgid="9084592086808194457">"ಯಾವುದೂ ಬೇಡ"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ಯಾವುದೇ ಆ್ಯಪ್‌ಗಳು ಇಲ್ಲ"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಫೋಟೋಗಳು ಮತ್ತು ಮೀಡಿಯಾವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮ್ಮ ಸಂಪರ್ಕಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"ಈ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ಸಾಧನದ ಸ್ಥಳವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ಸಾಧನದ ಸ್ಥಳವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ನ ಸ್ಥಳ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಬದಲಾಯಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಬಳಸದಿರುವಾಗಲೂ ಸಹ, ಯಾವಾಗಲೂ ನಿಮ್ಮ ಸ್ಥಳಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಲು ಆ್ಯಪ್ ಬಯಸುತ್ತದೆ. "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"ಸಮೀಪದ ಸಾಧನಗಳನ್ನು ಹುಡುಕಲು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ಅವುಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"ಸಮೀಪದ ಸಾಧನಗಳನ್ನು ಹುಡುಕಲು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ಅವುಗಳ ತುಲನಾತ್ಮಕ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಸಮೀಪದ ಸಾಧನ ಹುಡುಕಲು, ಕನೆಕ್ಟ್ ಆಗಲು, ಸಂಬಂಧಿತ ಸ್ಥಾನ ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"ಸಮೀಪದ ಸಾಧನಗಳನ್ನು ಹುಡುಕಲು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ಅವುಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ? "<annotation id="link">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಅನುಮತಿಸಿ."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ನ ಸ್ಥಳ ಪ್ರವೇಶವನ್ನು ಅಂದಾಜಿನಿಂದ ನಿಖರತೆಗೆ ಬದಲಾಯಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ಸಾಧನದ ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮ್ಮ ಕ್ಯಾಲೆಂಡರ್‌ ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು ಮತ್ತು ವೀಕ್ಷಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ 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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಫೋಟೋಗಳು, ಮೀಡಿಯಾ ಮತ್ತು ಫೈಲ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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;ಫೋಟೋಗಳು, ವೀಡಿಯೊಗಳು, ಸಂಗೀತ, ಆಡಿಯೋ, ಇತರ ಫೈಲ್‌ಗಳನ್ನು&lt;/b&gt; ಪ್ರವೇಶಿಸಲು &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಸಂಗೀತ ಮತ್ತು ಆಡಿಯೊವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"ಈ ಸಾಧನದಲ್ಲಿರುವ ಇನ್ನಷ್ಟು ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಇನ್ನಷ್ಟು ಫೋಟೋಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಆಡಿಯೋ ರೆಕಾರ್ಡ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ನ ಮೈಕ್ರೊಫೋನ್‌ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಬದಲಾಯಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮ್ಮ ದೈಹಿಕ ಚಟುವಟಿಕೆಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"ಚಿತ್ರಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್‌ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಚಿತ್ರಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಚಿತ್ರಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು ಮತ್ತು ವೀಡಿಯೊ ರೆಕಾರ್ಡ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ನ ಕ್ಯಾಮರಾ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಬದಲಾಯಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮ್ಮ ಫೋನ್ ಕರೆಯ ಲಾಗ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಲು ಹಾಗೂ ನಿರ್ವಹಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"ನಿಮ್ಮ ಅವಶ್ಯಕ ಶಾರೀರಿಕ ಕಾರ್ಯಗಳ ಚಿಹ್ನೆಗಳ ಕುರಿತ ಸೆನ್ಸರ್ ಡೇಟಾವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮ್ಮ ಆರೋಗ್ಯ ಮಾಪನಗಳ ಕುರಿತ ಸೆನ್ಸಾರ್ ಡೇಟಾ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮ್ಮ ಆರೋಗ್ಯ ಮಾಪನಗಳ ಕುರಿತ ಸೆನ್ಸಾರ್ ಡೇಟಾ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ಆ್ಯಪ್ ಬಳಸುವಾಗ ದೇಹದ ಸೆನ್ಸರ್‌ ಡೇಟಾ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸುತ್ತಿರಬೇಕೇ?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"ನಿಮಗೆ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಕಳುಹಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ನಲ್ಲಿ ನಿಮಗೆ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಕಳುಹಿಸಲು &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ಯಾವುದೂ ಅಲ್ಲ"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"ಕಳೆದ\n24 ಗಂಟೆಗಳು"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"ಕಳೆದ\n7 ದಿನಗಳಲ್ಲಿ"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ಶೇಕಡಾ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g>, Android ನಿಂದ ರಕ್ಷಣೆ ಪಡೆದಿದೆ. ಈ ಸಾಧನದಲ್ಲಿ ನಿಮ್ಮ ಡೇಟಾವನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗಿರುವುದರಿಂದ, ಈ ಆ್ಯಪ್‌ನ ಅನುಮತಿಯ ಬಳಕೆಯನ್ನು ಸ್ಥಿತಿ ಪಟ್ಟಿಯಲ್ಲಿ ಅಥವಾ ನಿಮ್ಮ ಗೌಪ್ಯತಾ ಡ್ಯಾಶ್‌ಬೋರ್ಡ್‌ನಲ್ಲಿ ತೋರಿಸಲಾಗುವುದಿಲ್ಲ."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"ಕ್ಯಾಮರಾ ಆ್ಯಕ್ಸೆಸ್ ಆಫ್ ಆಗಿದೆ"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"ಮೈಕ್ರೊಫೋನ್‌ ಆ್ಯಕ್ಸೆಸ್ ಆಫ್‌ ಆಗಿದೆ"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"ಸ್ಥಳದ ಆ್ಯಕ್ಸೆಸ್ ಆಫ್‌ ಆಗಿದೆ"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ಇನ್‌ಫೋಟೈನ್‌ಮೆಂಟ್ ಆ್ಯಪ್‌ಗಳಿಗಾಗಿ"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ಅಗತ್ಯವಿರುವ ಆ್ಯಪ್‌ಗಳಿಗಾಗಿ"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ಈ ಆ್ಯಪ್ ಅಗತ್ಯವಿದೆ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ನಿಮ್ಮ ಕಾರ್‌ನ ತಯಾರಕರಿಗೆ ಈ ಆ್ಯಪ್‌ ಅಗತ್ಯವಿದೆ"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> ಸಮಯಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ನಿನ್ನೆ <xliff:g id="TIME_DATE">%1$s</xliff:g> ಸಮಯಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ಸಮಯಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ನಿಮ್ಮ ಒನ್‌-ಟೈಮ್ ಪಾಸ್‌ವರ್ಡ್ 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ನಿರ್ಬಂಧಿಸಲಾದ ಸೆಟ್ಟಿಂಗ್"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ನಿಮ್ಮ ಸುರಕ್ಷತೆಗಾಗಿ, ಈ ಸೆಟ್ಟಿಂಗ್ ಪ್ರಸ್ತುತ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಆ್ಯಪ್‌ಗೆ ನಿರಾಕರಿಸಲಾಗಿದೆ"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮತ್ತು ಹಣಕಾಸಿನ ಮಾಹಿತಿಯನ್ನು ಅಪಾಯಕ್ಕೆ ಸಿಲುಕಿಸಬಹುದಾದ ಸೂಕ್ಷ್ಮ ಅನುಮತಿಗೆ ಆ್ಯಪ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ವಿನಂತಿಸಿದೆ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>.ಈ ನಿರ್ಬಂಧಿತ ಅನುಮತಿಯಿಲ್ಲದೆ ಆ್ಯಪ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರುವ ಸಾಧ್ಯತೆಯಿದೆ. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೇಗೆ ಅನುಮತಿಸುವುದು ಎಂಬುದನ್ನು ತಿಳಿಯಿರಿ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ಡೀಫಾಲ್ಟ್ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ಆಗಿರಲು ಆ್ಯಪ್‌ಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮತ್ತು ಹಣಕಾಸಿನ ಮಾಹಿತಿಯನ್ನು ಅಪಾಯಕ್ಕೆ ಸಿಲುಕಿಸಬಹುದಾದ ಸೂಕ್ಷ್ಮ ಅನುಮತಿಗಳಿಗೆ ಆ್ಯಪ್ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ವಿನಂತಿಸಿದೆ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ಈ ನಿರ್ಬಂಧಿತ ಅನುಮತಿಗಳಿಲ್ಲದೆ ಆ್ಯಪ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರುವ ಸಾಧ್ಯತೆಯಿದೆ. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೇಗೆ ಅನುಮತಿಸುವುದು ಎಂಬುದನ್ನು ತಿಳಿಯಿರಿ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ಆ್ಯಪ್‌ಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ಈ ಅನುಮತಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ನೀಡುವುದರಿಂದ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮತ್ತು ಹಣಕಾಸು ಮಾಹಿತಿಯು ಅಪಾಯಕ್ಕೆ ಸಿಲುಕಬಹುದು.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ಈ ನಿರ್ಬಂಧಿತ ಅನುಮತಿಯಿಲ್ಲದೆ ಆ್ಯಪ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರುವ ಸಾಧ್ಯತೆಯಿದೆ. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೇಗೆ ಅನುಮತಿಸುವುದು ಎಂಬುದನ್ನು ತಿಳಿಯಿರಿ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ಸರಿ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ನಿಗ್ರಹಿಸಲಾಗಿದೆ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ಈ ಆ್ಯಪ್ ಹೆಚ್ಚುವರಿ ಅನುಮತಿಗಳನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ, ಆದರೆ ಸ್ಟ್ರೀಮಿಂಗ್ ಸೆಶನ್‌ನಲ್ಲಿ ಅನುಮತಿಗಳನ್ನು ನೀಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮೊದಲು ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಅನುಮತಿ ನೀಡಿ."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ತುರ್ತು ಕರೆ ಅಥವಾ ಪಠ್ಯಕ್ಕಾಗಿ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ಸ್ಥಳವನ್ನು ತುರ್ತು ಸೇವೆಗಳಿಗೆ ಕಳುಹಿಸಲಾಗಿದೆ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ತುರ್ತು ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿದ ಅಥವಾ ಪಠ್ಯವನ್ನು ಕಳುಹಿಸುವ ಸಮಯದಲ್ಲಿ ಈ ಆ್ಯಪ್ ನಿಮ್ಮ ಸಾಧನದ ಸ್ಥಳವನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿದೆ. ಆ್ಯಪ್ ಸ್ಥಳದ ಅನುಮತಿಯನ್ನು ಹೊಂದಿಲ್ಲದಿರುವಾಗ ಅಥವಾ ಸಾಧನದ ಸ್ಥಳವು ಆಫ್ ಆಗಿರುವಾಗಲೂ ಇದು ಸಂಭವಿಸಬಹುದು. "<a href="https://support.google.com/android/answer/9319337">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</a></string>
</resources>
diff --git a/PermissionController/res/values-ko-v34/strings.xml b/PermissionController/res/values-ko-v34/strings.xml
index 7b3091170..6e3b42dbb 100644
--- a/PermissionController/res/values-ko-v34/strings.xml
+++ b/PermissionController/res/values-ko-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-ko-watch/strings.xml b/PermissionController/res/values-ko-watch/strings.xml
index fbe07c16c..89299a26f 100644
--- a/PermissionController/res/values-ko-watch/strings.xml
+++ b/PermissionController/res/values-ko-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"변경할 수 없음"</string>
<string name="generic_yes" msgid="2489207724988649846">"예"</string>
<string name="generic_cancel" msgid="2631708607129269698">"취소"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"항상"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"앱을 사용 중일 때"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"항상"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"앱을 사용 중일 때"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"항상"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"앱을 사용 중일 때"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"항상"</string>
</resources>
diff --git a/PermissionController/res/values-ko/strings.xml b/PermissionController/res/values-ko/strings.xml
index 4a5caa0e0..dc6e2e186 100644
--- a/PermissionController/res/values-ko/strings.xml
+++ b/PermissionController/res/values-ko/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"권한"</string>
<string name="cancel" msgid="8943320028373963831">"취소"</string>
<string name="back" msgid="6249950659061523680">"뒤로"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"닫기"</string>
<string name="available" msgid="6007778121920339498">"허용됨"</string>
<string name="blocked" msgid="9195547604866033708">"차단됨"</string>
<string name="on" msgid="280241003226755921">"사용"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear에서는 설치/제거 작업이 지원되지 않습니다"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 액세스하도록 허용할 항목 선택"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;이(가) 업데이트되었습니다. 이 앱에서 액세스하도록 허용할 항목을 선택하세요."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"취소"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"제한된 액세스 허용"</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_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_with_device_name" msgid="7193042925656173271">"이 앱의 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"사용하지 않는 앱 관리"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"권한 제거, 임시 파일 삭제, 알림 중지"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"권한 제거, 임시 파일 삭제, 알림 중지, 앱 보관처리"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"데이터 보호를 위해 몇 개월 동안 앱을 사용하지 않으면 앱의 권한이 삭제됩니다."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"데이터 보호를 위해 몇 개월 동안 앱을 사용하지 않으면 다음 권한이 삭제됩니다. <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"데이터 보호를 위해 몇 개월 동안 사용하지 않은 앱에서 권한이 삭제되었습니다."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"모든 파일을 관리하도록 허용됨"</string>
<string name="ask_header" msgid="2633816846459944376">"항상 확인"</string>
<string name="denied_header" msgid="903209608358177654">"허용되지 않음"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>의 <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"모든 파일에 액세스할 수 있는 앱 더보기"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1일}other{#일}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{#시간}other{#시간}}"</string>
@@ -340,7 +346,7 @@
<string name="no_apps_allowed" msgid="7718822655254468631">"허용된 앱 없음"</string>
<string name="no_apps_allowed_full" msgid="8011716991498934104">"모든 파일에 허용된 앱 없음"</string>
<string name="no_apps_allowed_scoped" msgid="4908850477787659501">"미디어에만 허용된 앱 없음"</string>
- <string name="no_apps_denied" msgid="7663435886986784743">"거부된 앱 없음"</string>
+ <string name="no_apps_denied" msgid="7663435886986784743">"허용되지 않은 앱 없음"</string>
<string name="car_permission_selected" msgid="180837028920791596">"선택됨"</string>
<string name="settings" msgid="5409109923158713323">"설정"</string>
<string name="accessibility_service_dialog_title_single" msgid="7956432823014102366">"<xliff:g id="SERVICE_NAME">%s</xliff:g>이(가) 기기에 대한 전체 액세스 권한을 가지고 있습니다."</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"기본 월렛 앱"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"월렛 앱"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"다양한 형태의 거래에 사용할 수 있도록 월렛 앱에 신용카드 및 포인트 카드, 자동차 키 등을 저장할 수 있습니다."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 기본 월렛 앱으로 설정하시겠습니까?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"권한 필요 없음"</string>
<string name="request_role_current_default" msgid="738722892438247184">"현재 기본 앱"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"다시 묻지 않음"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"기본 앱으로 설정"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"기본 앱 더보기"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"링크 열기"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"직장용 기본 앱"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"비공개 스페이스의 기본값"</string>
<string name="default_app_none" msgid="9084592086808194457">"없음"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(시스템 기본값)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"앱 없음"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;에서 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱의 위치 액세스 권한을 변경하시겠습니까?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"앱을 사용하고 있지 않을 때도 앱에서 내 위치에 항상 액세스하려고 합니다. "<annotation id="link">"설정에서 액세스를 허용"</annotation>"하세요."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;에서 근처에 있는 기기를 찾아 연결하고 기기 간 상대적 위치를 파악하도록 허용하시겠습니까?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 기기에서 <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;에서 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱의 마이크 액세스 권한을 변경하시겠습니까?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;에서 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱의 카메라 액세스 권한을 변경하시겠습니까?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;에서 생체 신호에 관한 센서 데이터에 액세스하도록 허용하시겠습니까?"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"앱을 사용하지 않을 때를 포함해 앱이 항상 생체 신호 센서 데이터에 액세스하도록 허용하려면 "<annotation id="link">"설정으로 이동"</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_device_aware_sensors" msgid="5542771499929819675">"앱을 사용하는 동안 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 앱이 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"없음"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"지난\n24시간"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"지난\n7일"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g>퍼센트"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱은 Android에 의해 보호됩니다. 데이터가 이 기기에서 처리되므로 앱 권한 사용 내역이 상태 표시줄이나 개인 정보 대시보드에 표시되지 않습니다."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"카메라 액세스 권한이 사용 중지됨"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"마이크 액세스 사용 중지됨"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"위치 액세스 사용 중지됨"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"인포테인먼트 앱"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"필수 앱"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"필수 앱"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"자동차 제조업체에서 요구한 필수 앱"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>에 액세스함"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"어제 <xliff:g id="TIME_DATE">%1$s</xliff:g>에 액세스함"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>에 액세스함"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"일회용 비밀번호는 132435입니다"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"제한된 설정"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"보안을 위해 이 설정은 현재 사용할 수 없습니다."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>에 대한 앱의 액세스가 거부됨"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"앱에서 개인 정보 및 금융 정보가 위험에 노출될 수 있는 민감한 권한에 대한 액세스를 요청했습니다.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;액세스 허용 방법 알아보기&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"앱을 기본 <xliff:g id="ROLE_NAME">%1$s</xliff:g>으로 사용하기 위한 액세스가 거부됨"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"앱에서 개인 정보 및 금융 정보가 위험에 노출될 수 있는 민감한 권한에 대한 액세스를 요청했습니다.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;액세스 허용 방법 알아보기&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"앱의 액세스가 거부됨"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"이 권한에 액세스하면 개인 정보 및 금융 정보가 위험에 노출될 수 있습니다.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>이 제한된 권한 없이는 앱이 제대로 작동하지 않을 수 있습니다. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;액세스 허용 방법 알아보기&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"자세히 알아보기"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"확인"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"권한 요청 거부됨"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"앱에서 추가 권한을 요청합니다. 그러나 스트리밍 세션에서는 권한을 부여할 수 없습니다. 휴대전화에서 먼저 권한을 부여하세요."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"긴급 전화 또는 문자용"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"위치를 응급 서비스로 전송함"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"긴급 전화번호로 전화를 걸거나 문자를 전송하는 중에 앱에서 기기 위치에 액세스했습니다. 앱에 위치 정보 액세스 권한이 없거나 기기 위치 기능이 사용 중지되어 있는 경우에도 앱에서 기기 위치에 액세스할 수 있습니다. "<a href="https://support.google.com/android/answer/9319337">"자세히 알아보기"</a></string>
</resources>
diff --git a/PermissionController/res/values-ky-v34/strings.xml b/PermissionController/res/values-ky-v34/strings.xml
index 15fc8424e..0753f01af 100644
--- a/PermissionController/res/values-ky-v34/strings.xml
+++ b/PermissionController/res/values-ky-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-ky-watch/strings.xml b/PermissionController/res/values-ky-watch/strings.xml
index d6ca47fc1..84b891bee 100644
--- a/PermissionController/res/values-ky-watch/strings.xml
+++ b/PermissionController/res/values-ky-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Өзгөртүүгө болбойт"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ооба"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Жок"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Ар дайым"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Колдонмону пайдаланууда"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Ар дайым"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Колдонмону пайдаланууда"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Ар дайым"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Колдонмону пайдаланууда"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Ар дайым"</string>
</resources>
diff --git a/PermissionController/res/values-ky/strings.xml b/PermissionController/res/values-ky/strings.xml
index fcfe39f44..9c8875086 100644
--- a/PermissionController/res/values-ky/strings.xml
+++ b/PermissionController/res/values-ky/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"уруксаттар"</string>
<string name="cancel" msgid="8943320028373963831">"Жок"</string>
<string name="back" msgid="6249950659061523680">"Артка"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Жабуу"</string>
<string name="available" msgid="6007778121920339498">"Жеткиликтүү"</string>
<string name="blocked" msgid="9195547604866033708">"Бөгөттөлдү"</string>
<string name="on" msgid="280241003226755921">"Күйүк"</string>
@@ -34,14 +35,15 @@
<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>
@@ -60,6 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Орнотуу/чыгарып салуу аракеттери Android Wear\'де колдоого алынбайт."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу үчүн уруксаттарды тандаңыз"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; жаңырды. Ал үчүн уруксаттарды тандаңыз."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Жок"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"Чектелген мүмкүнчүлүк берүү"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүндө бул колдонмо үчүн <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Колдонмо колдонулбаса, аны тескеңиз"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Уруксаттар өчүрүлүп, убактылуу файлдар тазаланып, билдирмелер келбей калат"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Уруксаттарды алып салып, убактылуу файлдарды жок кылып, билдирмелерди токтотуңуз жана колдонмону архивдеңиз"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Эгер колдонмо бир нече ай пайдаланылбаса, жеке маалыматтарыңызды коргоо үчүн бул колдонмого берилген уруксаттар өчүрүлөт."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Эгер колдонмо бир нече ай пайдаланылбаса, жеке дайын-даректериңизди коргоо максатында төмөнкү уруксаттар өчүрүлөт: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Жеке дайын-даректериңизди коргоо максатында, бир нече айдан бери ачылбаган колдонмолордогу уруксаттар өчүрүлдү."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Бардык файлдарды башкарууга уруксат берилген"</string>
<string name="ask_header" msgid="2633816846459944376">"Ар дайым суралсын"</string>
<string name="denied_header" msgid="903209608358177654">"Тыюу салынган"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>–<xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Дагы кайсы колдонмолорго бардык файлдар жеткиликтүү?"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 күн}other{# күн}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# саат}other{# саат}}"</string>
@@ -375,7 +381,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Демейки капчык колдонмосу"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Капчык колдонмосу"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Капчык колдонмосунда насыя жана туруктуу кардардын карталары, унаанын ачкычтары сыяктуу санарип нерселер сакталат. Алардын жардамы менен ар кандай транзакцияларды аткара аласыз."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> демейки капчык колдонмоңуз катары тууралансынбы?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Уруксаттардын кереги жок"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Учурдагы демейки колдонмо"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Экинчи суралбасын"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Демейки катары коюу"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Кошумча демейки колдонмолор"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Шилтемелерди ачуу"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Жумуш үчүн демейки жөндөөлөр"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Жеке мейкиндик үчүн демейки колдонмолор"</string>
<string name="default_app_none" msgid="9084592086808194457">"Жок"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Демейки тутум)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Бир да колдонмо жок"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; үчүн &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; үчүн &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; үчүн &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; түзмөгүндө организмдин негизги көрсөткүчтөрү тууралуу cенсордун дайындарын көрө берсинби?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосуна &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; түзмөгүндө организмдин негизги көрсөткүчтөрү тууралуу cенсордун дайындарын пайдаланууга уруксат бересизби?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонулуп жатканда ал &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; колдонмосу &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Жок"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Акыркы\n24 саат"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Акыркы\n7 күндө"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> пайыз"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android тарабынан корголот. Колдонмодогу нерселериңиз ушул түзмөктө иштетилгендиктен, бул колдонмонун уруксаттарды пайдалануу статистикасы абал тилкесинде же купуялык тактаңызда көрүнбөйт."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Камераны колдонуу мүмкүнчүлүгү өчүк"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Микрофонду колдонуу өчүк"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Жайгашкан жерди көрсөтүү өчүк"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Инфозоок колдонмолору үчүн"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Талап кылынган колдонмолор үчүн"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Бул колдонмо талап кылынат"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Бул колдонмону унааны өндүрүүчү милдеттүү деп белгилеген."</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>
@@ -581,7 +628,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,13 +657,32 @@
<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>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Параметрлер"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Cаат <xliff:g id="TIME_DATE">%1$s</xliff:g> колдонулду"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Кечээ саат <xliff:g id="TIME_DATE">%1$s</xliff:g> колдонулду"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> саат <xliff:g id="TIME_DATE_1">%2$s</xliff:g> колдонулду"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Бир жолу гана колдонулуучу кодуңуз: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Чектелген функция"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Коопсуздук максатында бул параметр азырынча иштебейт."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Колдонмого <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> параметрин колдонууга тыюу салынды"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Колдонмо жеке жана каржы маалыматыңызга коркунуч жаратышы мүмкүн болгон купуя маалыматты көрүүгө уруксат сурады.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Мындай уруксатсыз колдонмо ойдогудай иштебей коюшу мүмкүн. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Мүмкүнчүлүк берүү жөнүндө кеңири маалымат&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Колдонмого демейки <xliff:g id="ROLE_NAME">%1$s</xliff:g> ролуна тыюу салынды"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Колдонмо жеке жана каржы маалыматыңызга коркунуч жаратышы мүмкүн болгон купуя маалыматты көрүүгө уруксаттарды сурады.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Мындай чектелген уруксаттар берилбесе, колдонмо ойдогудай иштебеши ыктымал. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Мүмкүнчүлүк берүү жөнүндө кеңири маалымат&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Колдонмого мүмкүнчүлүк алууга тыюу салынды"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Бул уруксатты берсеңиз, жеке жана каржы маалыматыңызга коркунуч жаралышы мүмкүн.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Мындай уруксатсыз колдонмо ойдогудай иштебей коюшу мүмкүн. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Мүмкүнчүлүк берүү жөнүндө кеңири маалымат&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Кеңири маалымат"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Жарайт"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Уруксат берүү сурамы четке кагылды"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Бул колдонмо кошумча уруксаттарды берүүнү суранып жатат, бирок алып ойнотуу сеансында уруксаттарды берүүгө болбойт. Адегенде телефондо уруксат бериңиз."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Шашылыш чалуу же билдирүү жөнөтүү үчүн"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Жайгашкан жер кырсыктаганда жардамга келчү кызматтарга жөнөтүлдү"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Бул колдонмо кырсыктаганда жардамга келчү кызматтын номерине чалып же билдирүү жөнөткөнүңүздө түзмөк жайгашкан жерди аныктады. Колдонмонун жайгашкан жерди аныктоого уруксаты болбосо же түзмөктүн жайгашкан жери өчүк болсо да, ушундай болушу мүмкүн. "<a href="https://support.google.com/android/answer/9319337">"Кеңири маалымат"</a></string>
</resources>
diff --git a/PermissionController/res/values-lo-v34/strings.xml b/PermissionController/res/values-lo-v34/strings.xml
index 431b80ec1..07e965359 100644
--- a/PermissionController/res/values-lo-v34/strings.xml
+++ b/PermissionController/res/values-lo-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-lo-watch/strings.xml b/PermissionController/res/values-lo-watch/strings.xml
index f0c39134f..bc1fbad9f 100644
--- a/PermissionController/res/values-lo-watch/strings.xml
+++ b/PermissionController/res/values-lo-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ບໍ່ສາມາດປ່ຽນແປງໄດ້"</string>
<string name="generic_yes" msgid="2489207724988649846">"ໄປ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ຍົກເລີກ"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ຕະຫຼອດເວລາ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ໃນເວລາໃຊ້ແອັບ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ຕະຫຼອດເວລາ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ໃນເວລາໃຊ້ແອັບ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ຕະຫຼອດເວລາ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ໃນເວລາໃຊ້ແອັບ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ຕະຫຼອດເວລາ"</string>
</resources>
diff --git a/PermissionController/res/values-lo/strings.xml b/PermissionController/res/values-lo/strings.xml
index be8cf2947..23cb83500 100644
--- a/PermissionController/res/values-lo/strings.xml
+++ b/PermissionController/res/values-lo/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ສິດອະນຸຍາດ"</string>
<string name="cancel" msgid="8943320028373963831">"ຍົກເລີກ"</string>
<string name="back" msgid="6249950659061523680">"ກັບຄືນ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"ປິດ"</string>
<string name="available" msgid="6007778121920339498">"ສາມາດໃຊ້ໄດ້"</string>
<string name="blocked" msgid="9195547604866033708">"ບລັອກແລ້ວ"</string>
<string name="on" msgid="280241003226755921">"ເປີດຢູ່"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"ຕິດຕັ້ງ/ຖອນການຕິດຕັ້ງ ຄຳສັ່ງທີ່ບໍ່ຮອງຮັບຢູ່ Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"ເລືອກວ່າຈະອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຫຍັງໄດ້ແດ່"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"ອັບເດດ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ແລ້ວ. ກະລຸນາເລືອກວ່າຈະໃຫ້ແອັບນີ້ເຂົ້າເຖິງຫຍັງໄດ້ແດ່."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ຍົກເລີກ"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"ອະນຸຍາດສິດເຂົ້າເຖິງແບບຈຳກັດ"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> ເຂົ້າເຖິງແອັບນີ້ຢູ່ <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"ຈັດການແອັບຫາກບໍ່ໄດ້ໃຊ້"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ລຶບການອະນຸຍາດອອກ, ລຶບໄຟລ໌ຊົ່ວຄາວ ແລະ ຢຸດການແຈ້ງເຕືອນ"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ລຶບການອະນຸຍາດອອກ, ລຶບໄຟລ໌ຊົ່ວຄາວ, ຢຸດການແຈ້ງເຕືອນ ແລະ ເກັບແອັບໄວ້ໃນແຟ້ມ"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ເພື່ອປົກປ້ອງຂໍ້ມູນຂອງທ່ານ, ສິດອະນຸຍາດສຳລັບແອັບນີ້ຈະຖືກລຶບອອກຫາກບໍ່ໄດ້ໃຊ້ແອັບສອງສາມເດືອນ."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"ເພື່ອປົກປ້ອງຂໍ້ມູນຂອງທ່ານ, ຫາກບໍ່ໄດ້ໃຊ້ແອັບສອງສາມເດືອນ, ສິດອະນຸຍາດຕໍ່ໄປນີ້ຈະຖືກລຶບອອກ: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ເພື່ອປົກປ້ອງຂໍ້ມູນຂອງທ່ານ, ລະບົບໄດ້ລຶບສິດອະນຸຍາດອອກຈາກແອັບຕ່າງໆທີ່ທ່ານບໍ່ໄດ້ໃຊ້ສອງສາມເດືອນແລ້ວ."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ອະນຸຍາດໃຫ້ຈັດການໄຟລ໌ທັງໝົດແລ້ວ"</string>
<string name="ask_header" msgid="2633816846459944376">"ຖາມທຸກເທື່ອ"</string>
<string name="denied_header" msgid="903209608358177654">"ບໍ່ອະນຸຍາດ"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> ໃນ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ເບິ່ງແອັບເພີ່ມເຕີມທີ່ສາມາດເຂົ້າເຖິງໄຟລ໌ທັງໝົດໄດ້"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ມື້}other{# ມື້}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ຊົ່ວໂມງ}other{# ຊົ່ວໂມງ}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ແອັບ Wallet ເລີ່ມຕົ້ນ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"ແອັບ Wallet"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"ແອັບ Wallet ສາມາດຈັດເກັບບັດເຄຣດິດ ແລະ ບັດສະມາຊິກ, ກະແຈລົດ ແລະ ສິ່ງອື່ນໆຂອງທ່ານເພື່ອຊ່ວຍໃນການເຮັດທຸລະກຳຮູບແບບຕ່າງໆ."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"ຕັ້ງ <xliff:g id="APP_NAME">%1$s</xliff:g> ເປັນແອັບ Wallet ເລີ່ມຕົ້ນຂອງທ່ານບໍ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ບໍ່ຈຳເປັນຕ້ອງມີການອະນຸຍາດ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ຄ່າເລີ່ມຕົ້ນປັດຈຸບັນ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ບໍ່ຕ້ອງຖາມອີກ"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ຕັ້ງເປັນຄ່າເລີ່ມຕົ້ນ"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"ຄ່າເລີ່ມຕົ້ນເພີ່ມເຕີມ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ການເປີດລິ້ງ"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ຄ່າເລີ່ມຕົ້ນສຳລັບບ່ອນເຮັດວຽກ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ຄ່າເລີ່ມຕົ້ນສຳລັບພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
<string name="default_app_none" msgid="9084592086808194457">"ບໍ່ມີ"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ບໍ່ມີແອັບ"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຮູບພາບ ແລະ ສື່ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງລາຍຊື່ຜູ້ຕິດຕໍ່ຂອງທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງສະຖານທີ່ຂອງ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງສະຖານທີ່ຂອງ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"ປ່ຽນແປງສິດເຂົ້າເຖິງສະຖານທີ່ຂອງ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຊອກຫາ, ເຊື່ອມຕໍ່ ແລະ ລະບຸສະຖານທີ່ທີ່ກ່ຽວຂ້ອງກັນຂອງອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"ປ່ຽນສິດເຂົ້າເຖິງສະຖານທີ່ຂອງ <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງສະຖານທີ່ໂດຍປະມານຂອງ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງປະຕິທິນຂອງທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ສົ່ງ ແລະ ເບິ່ງຂໍ້ຄວາມ SMS ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຮູບພາບ, ສື່ ແລະ ໄຟລ໌ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງເພງ ແລະ ສຽງຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຮູບພາບ ແລະ ວິດີໂອຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຮູບພາບ ແລະ ວິດີໂອເພີ່ມເຕີມຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ບັນທຶກສຽງຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ບັນທຶກສຽງຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"ປ່ຽນແປງສິດເຂົ້າເຖິງໄມໂຄຣໂຟນຂອງ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງກິດຈະກຳທາງກາຍະພາບຂອງທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຖ່າຍຮູບ ແລະ ບັນທຶກວິດີໂອຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"ປ່ຽນແປງສິດເຂົ້າເຖິງກ້ອງຂອງ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງບັນທຶກການໂທໃນໂທລະສັບຂອງທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ໂທ ແລະ ຈັດການການໂທຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີກ່ຽວກັບສັນຍານຊີບຂອງທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີກ່ຽວກັບສັນຍານຊີບຂອງທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ບໍ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ເພື່ອເຮັດໃຫ້ແອັບນີ້ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີຮ່າງກາຍໄດ້ຕະຫຼອດເວລາ, ເຖິງແມ່ນວ່າທ່ານຈະບໍ່ໄດ້ກຳລັງໃຊ້ແອັບຢູ່ກໍຕາມ, "<annotation id="link">"ໃຫ້ເຂົ້າໄປການຕັ້ງຄ່າ."</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_device_aware_sensors" msgid="5542771499929819675">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ເຂົ້າເຖິງຂໍ້ມູນເຊັນເຊີຮ່າງກາຍໄດ້ຕະຫຼອດໃນລະຫວ່າງທີ່ໃຊ້ແອັບຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"ອະນຸຍາດໃຫ້ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ສົ່ງການແຈ້ງເຕືອນໃຫ້ທ່ານຢູ່ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ບໍ່ມີ"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 ຊົ່ວໂມງ\nທີ່ຜ່ານມາ"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 ມື້\nທີ່ຜ່ານມາ"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ເປີເຊັນ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ແມ່ນຖືກປົກປ້ອງໂດຍ Android. ເນື່ອງຈາກຂໍ້ມູນຂອງທ່ານຖືກປະມວນຜົນຢູ່ອຸປະກອນນີ້, ການນຳໃຊ້ການອະນຸຍາດຂອງແອັບນີ້ຈະບໍ່ສະແດງຢູ່ແຖບສະຖານະ ຫຼື ແຜງໜ້າປັດຄວາມເປັນສ່ວນຕົວຂອງທ່ານ."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບປິດຢູ່"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"ສິດເຂົ້າເຖິງໄມໂຄຣໂຟນປິດຢູ່"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"ສິດເຂົ້າເຖິງສະຖານທີ່ປິດຢູ່"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ສຳລັບແອັບສາລະບັນເທີງ"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ສຳລັບແອັບທີ່ຈຳເປັນ"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ແອັບນີ້ຈຳເປັນ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ແອັບນີ້ຈຳເປັນໂດຍຜູ້ຜະລິດລົດຂອງທ່ານ"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"ເຂົ້າເຖິງເມື່ອ <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ເຂົ້າເຖິງມື້ວານນີ້ເມື່ອ <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"ເຂົ້າເຖິງເມື່ອ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ລະຫັດຜ່ານແບບໃຊ້ໄດ້ເທື່ອດຽວຂອງທ່ານແມ່ນ 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ການຕັ້ງຄ່າທີ່ຈຳກັດ"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ຕອນນີ້ຈຶ່ງບໍ່ສາມາດໃຊ້ການຕັ້ງຄ່ານີ້ໄດ້."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງຫາ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ແອັບໄດ້ຮ້ອງຂໍການສິດເຖິງການອະນຸຍາດທີ່ລະອຽດອ່ອນ ເຊິ່ງສາມາດເຮັດໃຫ້ຂໍ້ມູນສ່ວນຕົວ ແລະ ຂໍ້ມູນການເງິນຂອງທ່ານມີຄວາມສ່ຽງ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ມັນເປັນໄປໄດ້ວ່າແອັບດັ່ງກ່າວຈະບໍ່ເຮັດວຽກຢ່າງຖືກຕ້ອງໂດຍບໍ່ມີການອະນຸຍາດທີ່ຖືກຈຳກັດໄວ້ນີ້. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ສຶກສາວິທີອະນຸຍາດສິດເຂົ້າເຖິງ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງໃຫ້ເປັນ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ຄ່າເລີ່ມຕົ້ນ"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"ແອັບໄດ້ຮ້ອງຂໍສິດເຂົ້າເຖິງການອະນຸຍາດທີ່ລະອຽດອ່ອນ ເຊິ່ງສາມາດເຮັດໃຫ້ຂໍ້ມູນສ່ວນຕົວ ແລະ ຂໍ້ມູນການເງິນຂອງທ່ານມີຄວາມສ່ຽງ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ມັນເປັນໄປໄດ້ວ່າແອັບດັ່ງກ່າວຈະບໍ່ເຮັດວຽກຢ່າງຖືກຕ້ອງໂດຍບໍ່ມີການອະນຸຍາດທີ່ຖືກຈຳກັດໄວ້ເຫຼົ່ານີ້. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ສຶກສາວິທີອະນຸຍາດສິດເຂົ້າເຖິງ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ແອັບຖືກປະຕິເສດສິດເຂົ້າເຖິງ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ເຂົ້າເຖິງການອະນຸຍາດນີ້ສາມາດເຮັດໃຫ້ຂໍ້ມູນສ່ວນຕົວ ແລະ ຂໍ້ມູນການເງິນຂອງທ່ານມີຄວາມສ່ຽງ.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ມັນເປັນໄປໄດ້ວ່າແອັບດັ່ງກ່າວຈະບໍ່ເຮັດວຽກຢ່າງຖືກຕ້ອງໂດຍບໍ່ມີການອະນຸຍາດທີ່ຖືກຈຳກັດໄວ້ນີ້. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ສຶກສາວິທີອະນຸຍາດສິດເຂົ້າເຖິງ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ສຶກສາເພີ່ມເຕີມ"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ຕົກລົງ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ຄຳຮ້ອງຂໍການອະນຸຍາດຖືກລະງັບ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ແອັບນີ້ກຳລັງຮ້ອງຂໍການອະນຸຍາດເພີ່ມເຕີມ, ແຕ່ບໍ່ສາມາດໃຫ້ການອະນຸຍາດໃນເຊດຊັນການສະຕຣີມໄດ້. ກະລຸນາໃຫ້ການອະນຸຍາດໃນໂທລະສັບຂອງທ່ານກ່ອນ."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ສຳລັບການໂທ ຫຼື ສົ່ງຂໍ້ຄວາມສຸກເສີນ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ສົ່ງສະຖານທີ່ໄປຫາບໍລິການສຸກເສີນແລ້ວ"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ແອັບນີ້ໄດ້ເຂົ້າເຖິງສະຖານທີ່ຂອງອຸປະກອນຂອງທ່ານໃນລະຫວ່າງທີ່ໂທ ຫຼື ສົ່ງຂໍ້ຄວາມຫາເບີໂທສຸກເສີນ. ເຊິ່ງນີ້ສາມາດເກີດຂຶ້ນໄດ້ເຖິງແມ່ນວ່າແອັບຈະບໍ່ມີການອະນຸຍາດສະຖານທີ່ ຫຼື ເຖິງແມ່ນວ່າສະຖານທີ່ຂອງອຸປະກອນຈະປິດຢູ່ກໍຕາມ. "<a href="https://support.google.com/android/answer/9319337">"ສຶກສາເພີ່ມເຕີມ"</a></string>
</resources>
diff --git a/PermissionController/res/values-lt-v34/strings.xml b/PermissionController/res/values-lt-v34/strings.xml
index f7a60d198..2546f96e5 100644
--- a/PermissionController/res/values-lt-v34/strings.xml
+++ b/PermissionController/res/values-lt-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Programos prieigos prie sveikatos duomenų tvarkymas"</string>
<string name="location_settings" msgid="8863940440881290182">"Prieiga prie vietovės"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Programos ir paslaugos. Jei šis nustatymas išjungtas, mikrofono duomenys vis tiek gali būti bendrinami, skambinant pagalbos numeriu"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Programos ir paslaugos"</string>
</resources>
diff --git a/PermissionController/res/values-lt-watch/strings.xml b/PermissionController/res/values-lt-watch/strings.xml
index c852a6508..b875ee94e 100644
--- a/PermissionController/res/values-lt-watch/strings.xml
+++ b/PermissionController/res/values-lt-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Negalima pakeisti"</string>
<string name="generic_yes" msgid="2489207724988649846">"Taip"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Atšaukti"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Visą laiką"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Kai programa naudojama"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Visą laiką"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Kai programa naudojama"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Visą laiką"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Kai programa naudojama"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Visą laiką"</string>
</resources>
diff --git a/PermissionController/res/values-lt/strings.xml b/PermissionController/res/values-lt/strings.xml
index 1c179457d..cf632afd4 100644
--- a/PermissionController/res/values-lt/strings.xml
+++ b/PermissionController/res/values-lt/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"leidimai"</string>
<string name="cancel" msgid="8943320028373963831">"Atšaukti"</string>
<string name="back" msgid="6249950659061523680">"Atgal"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Uždaryti"</string>
<string name="available" msgid="6007778121920339498">"Pasiekiama"</string>
<string name="blocked" msgid="9195547604866033708">"Užblokuota"</string>
<string name="on" msgid="280241003226755921">"Įjungta"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Visi leidimai"</string>
<string name="other_permissions" msgid="2901186127193849594">"Kitos programos galimybės"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Leidimo užklausa"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Diegimo / pašalinimo veiksmai nepalaikomi sistemoje „Wear“."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Pasirinkite, ką norite leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Programa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; buvo atnaujinta. Pasirinkite, ką norite leisti šiai programai pasiekti."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Atšaukti"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Visada leisti viską"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Klausti kaskart"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Neleisti"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Suteikti ribotą prieigą"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Tiksli vietovė"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Apytikslė vietovė"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Tikslios vietovės naudojimas"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kai tikslios vietovės nustatymo parinktis išjungta, programos gali pasiekti apytikslės vietovės duomenis"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Leidimas: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>: šios programos prieiga"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g> prieiga šiai programai „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Žr. visus „<xliff:g id="APP">%1$s</xliff:g>“ leidimus"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Žr. visas programas, kurioms suteiktas šis leidimas"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Rodyti Padėjėjo mikrofono naudojimą"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Pašalinti leidimus, jei programa nenaudojama"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Pašalinti leidimus ir atlaisvinti vietos"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pristabdyti nenaudojamų programų veiklą"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Tvarkyti programą, jei nenaudojama"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Pašalinkite leidimus, ištrinkite laikinus failus ir sustabdykite pranešimus"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Pašalinti leidimus, ištrinti laikinus failus, sustabdyti pranešimus ir archyvuoti programą"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Siekiant apsaugoti duomenis, šios programos leidimai bus pašalinti, jei programos nenaudosite kelis mėnesius."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Siekiant apsaugoti duomenis, jei programos nenaudosite kelis mėnesius, bus pašalinti nurodyti leidimai: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Siekiant apsaugoti duomenis, leidimai buvo pašalinti iš programų, kurių nenaudojote kelis mėnesius"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Leidžiama valdyti visus failus"</string>
<string name="ask_header" msgid="2633816846459944376">"Klausti kaskart"</string>
<string name="denied_header" msgid="903209608358177654">"Neleidžiama"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Peržiūrėti daugiau programų, galinčių pasiekti visus failus"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 diena}one{# diena}few{# dienos}many{# dienos}other{# dienų}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# valanda}one{# valanda}few{# valandos}many{# valandos}other{# valandų}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Užrašų programa"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Programos, leidžiančios rašyti užrašus jūsų įrenginyje"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"užrašai"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Numatytoji piniginės programa"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Piniginės programa"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Piniginės programos gali saugoti jūsų kredito ir lojalumo kortelių, automobilių raktų ir kitus duomenis, kad lengviau atliktumėte įvairias operacijas."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Nustatyti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ kaip numatytąją piniginės programą?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nereikia jokių leidimų"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Dabartinė numatytoji"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Daugiau neklausti"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Nustatyti numatytąja"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Daugiau numatytųjų programų"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Nuorodų atidarymas"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Numatytosios darbo programos"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Privačios erdvės numatytosios programos"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nėra"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Sistemos numatytoji programa)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nėra programų"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Rodyti padėjėjo aktyviklio aptikimą"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Rodyti piktogramą būsenos juostoje, kai naudojant mikrofoną aktyvinama pagalba balsu"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti įrenginio nuotraukas ir mediją?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti nuotraukas ir mediją įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti kontaktus?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti kontaktus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti įrenginio vietovę?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti įrenginio &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; vietovę?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Programa galės pasiekti vietovę, tik kai ją naudosite"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti įrenginio vietovę?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti įrenginio &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; vietovę?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ši programa gali norėti pasiekti jūsų vietovę visą laiką, net kai programos nenaudojate. "<annotation id="link">"Leiskite skiltyje „Nustatymai“."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Pakeisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prieigą prie vietovės duomenų?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Keisti programos &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prieigą prie vietovės įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ši programa nori pasiekti jūsų vietovę visą laiką, net kai programos nenaudojate. "<annotation id="link">"Leiskite skiltyje „Nustatymai“."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; rasti netoliese esančius įrenginius, prisijungti prie jų ir nustatyti apytikslį atstumą?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; rasti apytikslę įrenginių netoliese vietą, aptikti juos ir prisijungti įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; rasti netoliese esančius įrenginius, prisijungti prie jų ir nustatyti apytikslį atstumą? "<annotation id="link">"Leiskite nustatymuose."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Pakeisti <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> vietovės pasiekiamumą iš apytikslės į tikslią?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Leisti programos „<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>“ prieigą prie vietovės informacijos įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; iš apytikslės į tikslią?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti apytikslę šio įrenginio vietovę?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti apytikslę įrenginio &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; vietovę?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Tiksli"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Apytikslė"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti kalendorių?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti kalendorių įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siųsti ir peržiūrėti SMS pranešimus?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siųsti ir peržiūrėti SMS pranešimus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti įrenginio nuotraukas, mediją ir failus?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti nuotraukas, mediją ir failus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti &lt;b&gt;nuotraukas, vaizdo, garso įrašus ir muziką&lt;/b&gt; šiame įrenginyje?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti &lt;b&gt;nuotrauk., vaizdo, garso įrašus, muziką, kitus failus&lt;/b&gt; įrenginyje?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti muziką ir garso failus šiame įrenginyje?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti muziką ir garso failus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti nuotraukas ir vaizdo įrašus šiame įrenginyje?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti nuotraukas ir vaizdo įrašus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti nuotraukas ir vaizdo įrašus šiame įrenginyje?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti daugiau nuotraukų ir vaizdo įrašų įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Programa galės įrašyti garsą, tik kai ją naudosite"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; įrašyti garsą įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ši programa gali norėti įrašyti garsą visą laiką, net kai programos nenaudojate. "<annotation id="link">"Leiskite skiltyje „Nustatymai“."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Pakeisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prieigą prie mikrofono?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Keisti programos &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prieigą prie mikrofono įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ši programa nori įrašyti garsą visą laiką, net kai programos nenaudojate. "<annotation id="link">"Leiskite skiltyje „Nustatymai“."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti fizinės veiklos duomenis?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti fizinės veiklos duomenis įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotografuoti ir įrašyti vaizdo įrašus?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotografuoti ir filmuoti įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Programa galės fotografuoti ir įrašyti vaizdo įrašų, tik kai ją naudosite"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotografuoti ir įrašyti vaizdo įrašus?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotografuoti ir filmuoti įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ši programa gali norėti fotografuoti ir įrašyti vaizdo įrašų visą laiką, net kai programos nenaudojate. "<annotation id="link">"Leiskite skiltyje „Nustatymai“."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Pakeisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prieigą prie fotoaparato?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Keisti programos &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prieigą prie fotoaparato įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ši programa nori fotografuoti ir įrašyti vaizdo įrašų visą laiką, net kai programos nenaudojate. "<annotation id="link">"Leiskite skiltyje „Nustatymai“."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti jūsų telefono skambučių žurnalus?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti telefonų skambučių žurnalus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; skambinti ir tvarkyti telefono skambučius?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; skambinti ir tvarkyti telefonų skambučius įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti jutiklių duomenis apie gyvybinius ženklus?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti jutiklių duomenis apie gyvybinių funkcijų rodiklius įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ši programa nori pasiekti jutiklių duomenis apie jūsų gyvybinių funkcijų rodiklius visą laiką, net kai programos nenaudojate. Kad atliktumėte šį pakeitimą, "<annotation id="link">"eikite į skiltį „Nustatymai“."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Suteikti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; galimybę pasiekti jutiklių duomenis apie gyvybinių funkcijų rodiklius?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti jutiklių duomenis apie gyvybinių funkcijų rodiklius įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Jei norite leisti šiai programai pasiekti kūno jutiklių duomenis visą laiką, net kai nenaudojate programos, "<annotation id="link">"eikite į „Nustatymų“ skiltį"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Toliau leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti kūno jutiklių duomenis, kai programa naudojama?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Toliau leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pasiekti kūno jutiklių duomenis įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;, kol naudojama programa?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Leisti &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siųsti jums pranešimus?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Leisti programai &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siųsti jums pranešimus įrenginyje &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Valdomi leidimai"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ gali pasiekti vietovę"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Jūsų organizacija leidžia programai „<xliff:g id="APP_NAME">%1$s</xliff:g>“ pasiekti jūsų vietovę"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nėra"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Pastarosios\n24 val."</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Pastarosios\n7 dienos"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> proc."</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ apsaugota „Android“. Kadangi duomenys apdorojami šiame įrenginyje, šios programos leidimo naudojimas nerodomas būsenos juostoje ar privatumo informacijos suvestinėje."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ apsaugota „Android“. Kadangi duomenys apdorojami šiame įrenginyje, šios programos leidimo naudojimas nerodomas privatumo informacijos suvestinėje."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Įrenginio vaizdo kamera užblokuota"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Programos ir paslaugos"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofono duomenys vis tiek gali būti bendrinami, skambinant pagalbos numeriu."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Pakeisti"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Prieiga prie vaizdo kameros išjungta"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Prieiga prie mikrofono išjungta"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Prieiga prie vietovės informacijos išjungta"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Skirta informacinės pramoginės sistemos programoms"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Skirta būtinoms programoms"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ši programa yra būtina"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Šios programos reikalauja automobilio gamintojas"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sauga ir privatumas"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Nuskaityti įrenginį"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Atsisakyti"</string>
@@ -609,7 +656,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Duomenų bendrinimo naujiniai"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Kai kuriose programose pakeisti vietovės duomenų bendrinimo metodai"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Nustatymai"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Pasiekta <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pasiekta vakar <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pasiekta <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Jūsų vienkartinis slaptažodis yra 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Apribotas nustatymas"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Šis nustatymas šiuo metu nepasiekiamas dėl jūsų saugumo."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Programai prieiga nesuteikta:<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Programa pateikė leidimo pasiekti neskelbtiną informaciją, dėl kurio gali kilti pavojus jūsų asmens ir finansinei informacijai, užklausą.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Gali būti, kad be šio apriboto leidimo programa neveiks tinkamai. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Sužinokite, kaip suteikti prieigą&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Programai nesuteikta prieiga kaip numatytojo šio vaidmens: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Programa pateikė leidimo pasiekti neskelbtiną informaciją, dėl kurio gali kilti pavojus jūsų asmens ir finansinei informacijai, užklausą.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Gali būti, kad be šių apribotų leidimų programa neveiks tinkamai. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Sužinokite, kaip suteikti prieigą&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Programai prieiga nesuteikta"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Prieiga prie šio leidimo gali kelti pavojų jūsų asmens ir finansinei informacijai.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Gali būti, kad be šio apriboto leidimo programa neveiks tinkamai. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Sužinokite, kaip suteikti prieigą&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Sužinokite daugiau"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Gerai"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Leidimo užklausa atmesta"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ši programa prašo papildomų leidimų, bet jų negalima suteikti per srautinio perdavimo seansą. Leidimą pirmiausia suteikite telefone."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Skambutis arba pranešimas pagalbos numeriu"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Vietovės informacija išsiųsta pagalbos tarnyboms"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Skambinant ar siunčiant pranešimą pagalbos numeriu, ši programa pasiekė jūsų įrenginio vietovės informaciją. Taip gali nutikti net tada, kai programa neturi leidimo pasiekti vietovės duomenis arba kai įrenginio vietovė yra išjungta."<a href="https://support.google.com/android/answer/9319337">"Sužinokite daugiau"</a></string>
</resources>
diff --git a/PermissionController/res/values-lv-v34/strings.xml b/PermissionController/res/values-lv-v34/strings.xml
index 3c99e3275..649416a32 100644
--- a/PermissionController/res/values-lv-v34/strings.xml
+++ b/PermissionController/res/values-lv-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Pārvaldiet lietotņu piekļuvi veselības datiem."</string>
<string name="location_settings" msgid="8863940440881290182">"Piekļuve atrašanās vietai"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Lietotnēm un pakalpojumiem. Ja šis iestatījums ir izslēgts, mikrofona dati joprojām var tikt kopīgoti, kad zvanīsiet uz ārkārtas numuru."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Lietotnēm un pakalpojumiem"</string>
</resources>
diff --git a/PermissionController/res/values-lv-watch/strings.xml b/PermissionController/res/values-lv-watch/strings.xml
index 7ee4ab980..b3ca497f6 100644
--- a/PermissionController/res/values-lv-watch/strings.xml
+++ b/PermissionController/res/values-lv-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nevar mainīt"</string>
<string name="generic_yes" msgid="2489207724988649846">"Jā"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Atcelt"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Vienmēr"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Izmantojot lietotni"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Vienmēr"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Izmantojot lietotni"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Vienmēr"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Izmantojot lietotni"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Vienmēr"</string>
</resources>
diff --git a/PermissionController/res/values-lv/strings.xml b/PermissionController/res/values-lv/strings.xml
index 530e2cc6c..35b3ea108 100644
--- a/PermissionController/res/values-lv/strings.xml
+++ b/PermissionController/res/values-lv/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"atļaujas"</string>
<string name="cancel" msgid="8943320028373963831">"Atcelt"</string>
<string name="back" msgid="6249950659061523680">"Atpakaļ"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Pieejama"</string>
<string name="blocked" msgid="9195547604866033708">"Bloķēta"</string>
<string name="on" msgid="280241003226755921">"Ieslēgta"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Visas atļaujas"</string>
<string name="other_permissions" msgid="2901186127193849594">"Citas lietotnes atļaujas"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Atļaujas pieprasījums"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear ierīcē netiek atbalstīta instalēšana/atinstalēšana"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Izvēlieties, kādas piekļuves atļaujas piešķirt lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Lietotne &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ir atjaunināta. Izvēlieties, kādas piekļuves atļaujas tai piešķirt."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Atcelt"</string>
@@ -191,12 +193,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Vienmēr atļaut visu"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Vaicāt katru reizi"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Neatļaut"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Atļaut ierobežotu piekļuvi"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Precīza atrašanās vieta"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Aptuvena atrašanās vieta"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Izmantot precīzu atrašanās vietu"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Ja precīzās atrašanās vietas noteikšana ir izslēgta, lietotnes var piekļūt jūsu aptuvenajai atrašanās vietai."</string>
<string name="app_permission_title" msgid="2090897901051370711">"Atļauja: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>: šīs lietotnes piekļuve"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Šai lietotnei ir piešķirta piekļuves atļauja “<xliff:g id="PERM">%1$s</xliff:g>” šajā ierīcē: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Skatīt visas lietotnei <xliff:g id="APP">%1$s</xliff:g> piešķirtās atļaujas"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Skatīt visas lietotnes, kam ir šī atļauja"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Rādīt Asistenta mikrofona lietojumu"</string>
@@ -204,7 +208,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Noņemt atļaujas, ja lietotne netiek izmantota"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Noņemt atļaujas un atbrīvot vietu"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Apturēt lietotni, ja tā netiek izmantota"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Pārvaldīt lietotni, ja tā netiek lietota"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Atsaukt atļaujas, dzēst pagaidu failus un izslēgt paziņojumus"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Noņemt atļaujas, dzēst pagaidu failus, apturēt paziņojumus un arhivēt lietotni"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Ja lietotni dažus mēnešus neizmantosiet, tai tiks noņemtas tālāk norādītās atļaujas, lai aizsargātu jūsu datus."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ja lietotni dažus mēnešus neizmantosiet, tai tiks noņemtas tālāk norādītās atļaujas, lai aizsargātu jūsu datus: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Lai aizsargātu jūsu datus, tika atsauktas atļaujas tām lietotnēm, kas nav izmantotas vairākus mēnešus."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Atļauts pārvaldīt visus failus"</string>
<string name="ask_header" msgid="2633816846459944376">"Vaicāt katru reizi"</string>
<string name="denied_header" msgid="903209608358177654">"Nav atļauts"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"Atļauja <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> šajā ierīcē: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Skatīt citas lietotnes, kas drīkst piekļūt visiem failiem"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 diena}zero{# dienu}one{# diena}other{# dienas}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# stunda}zero{# stundu}one{# stunda}other{# stundas}}"</string>
@@ -401,6 +408,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Piezīmju lietotne"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Lietotnes, kas ļauj ierīcē veikt piezīmes"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"piezīmes"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Noklusējuma maka lietotne"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Maka lietotne"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Maka lietotnēs var glabāt informāciju par kredītkartēm un lojalitātes kartēm, automašīnas šifratslēgas un citu informāciju, kas palīdz veikt dažādu veidu darījumus."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Vai iestatīt lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> kā noklusējuma maka lietotni?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nav nepieciešamas nekādas atļaujas."</string>
<string name="request_role_current_default" msgid="738722892438247184">"Pašreizējais noklusējums"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Nejautāt atkārtoti"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Iest. kā noklusējumu"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Citas noklusējuma lietotnes"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Saišu atvēršana"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Noklusējuma iestatījums darbam"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Noklusējums privātajai telpai"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nav"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Sistēmas noklusējums)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nav lietotņu"</string>
@@ -455,48 +468,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Rādīt asistenta aktivizētāja noteikšanu"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Rādīt ikonu statusa joslā, kad mikrofons tiek izmantots balss palīga aktivizēšanai"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem un multivides saturam jūsu ierīcē?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem un multivides saturam ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu kontaktpersonām?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu kontaktpersonām ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt šīs ierīces atrašanās vietai?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt ierīces &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; atrašanās vietai?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Lietotne varēs piekļūt atrašanās vietai tikai tad, kad izmantosiet šo lietotni"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt šīs ierīces atrašanās vietai?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt ierīces &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; atrašanās vietai?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Iespējams, šī lietotne vēlēsies piekļūt jūsu atrašanās vietai vienmēr, pat ja neizmantojat lietotni. "<annotation id="link">"Atļauju varat piešķirt iestatījumos"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Vai mainīt lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļuvi atrašanās vietai?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Vai mainīt atrašanās vietas piekļuves atļauju lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Šī lietotne vēlas piekļūt jūsu atrašanās vietai vienmēr, pat ja neizmantojat lietotni. "<annotation id="link">"Atļauju varat piešķirt iestatījumos"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Vai atļaut &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atrast tuvumā esošas ierīces, veidot savienojumus ar tām un noteikt to relatīvo atrašanās vietu?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; atrast tuvumā esošas ierīces, veidot savienojumus ar tām un noteikt to relatīvo atrašanās vietu?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Vai atļaut &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; atrast tuvumā esošas ierīces, veidot savienojumus ar tām un noteikt to relatīvo atrašanās vietu? "<annotation id="link">"Varat to atļaut iestatījumos."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Vai mainīt lietotnes <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> piekļuvi atrašanās vietai no aptuvenās uz precīzo?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Vai mainīt lietotnes <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> atrašanās vietas piekļuves atļauju ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; no aptuvenas uz precīzu?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt aptuvenai šīs ierīces atrašanās vietai?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt ierīces &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; aptuvenai atrašanās vietai?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Precīza"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aptuvena"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu kalendāram?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu kalendāram ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sūtīt un skatīt īsziņas?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sūtīt un skatīt īsziņas ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem, multivides saturam un failiem jūsu ierīcē?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem, multivides saturam un failiem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt &lt;b&gt;foto, video, mūzikai un audio failiem&lt;/b&gt; šajā ierīcē?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt &lt;b&gt;foto, video, mūzikai, audio u.c. failiem&lt;/b&gt; ierīcē?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt mūzikai un audio failiem šajā ierīcē?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt mūzikai un audio ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem un video šajā ierīcē?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fotoattēliem un videoklipiem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt citiem fotoattēliem un video šajā ierīcē?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt citiem fotoattēliem un videoklipiem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Lietotne varēs ierakstīt audio tikai tad, kad izmantosiet lietotni."</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierakstīt audio ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Iespējams, šī lietotne vēlēsies ierakstīt audio vienmēr, pat ja neizmantojat lietotni. "<annotation id="link">"Atļauju varat piešķirt iestatījumos."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Vai mainīt lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļuvi mikrofonam?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Vai mainīt mikrofona piekļuves atļauju lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Šī lietotne vēlas ierakstīt audio vienmēr, pat ja neizmantojat lietotni. "<annotation id="link">"Atļauju varat piešķirt iestatījumos."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu fiziskajām aktivitātēm?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt fizisko aktivitāšu datiem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uzņemt fotoattēlus un ierakstīt videoklipus?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uzņemt fotoattēlus un ierakstīt videoklipus ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Lietotne varēs uzņemt attēlus un ierakstīt videoklipus tikai tad, kad izmantosiet lietotni."</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uzņemt fotoattēlus un ierakstīt videoklipus?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uzņemt fotoattēlus un ierakstīt videoklipus ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Iespējams, šī lietotne vēlēsies uzņemt attēlus un ierakstīt videoklipus vienmēr, pat ja neizmantojat lietotni. "<annotation id="link">"Atļauju varat piešķirt iestatījumos."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Vai mainīt lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļuvi kamerai?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Vai mainīt kameras piekļuves atļauju lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Šī lietotne vēlas uzņemt attēlus un ierakstīt videoklipus vienmēr, pat ja neizmantojat lietotni. "<annotation id="link">"Atļauju varat piešķirt iestatījumos."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu tālruņa zvanu žurnāliem?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt jūsu tālruņa zvanu žurnāliem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; veikt un pārvaldīt tālruņa zvanus?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; veikt un pārvaldīt tālruņa zvanus ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt sensoru uztvertajiem veselības rādījumiem?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt sensoru datiem par veselības rādījumiem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Lietotne pieprasa atļauju piekļūt veselības rādījumu sensoru datiem vienmēr, pat ja neizmantojat lietotni. Lai veiktu šīs izmaiņas, "<annotation id="link">"pārejiet uz iestatījumiem"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt veselības rādītāju sensoru datiem?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt sensoru datiem par veselības rādījumiem ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Lai atļautu šai lietotnei piekļūt ķermeņa sensoru datiem vienmēr (pat tad, kad neizmantojat lietotni), "<annotation id="link">"pārejiet uz iestatījumiem"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Vai joprojām atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt ķermeņa sensoru datiem, kad izmantojat lietotni?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Vai arī turpmāk atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; piekļūt ierīces &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ķermeņa sensora datiem, kamēr lietotne tiek izmantota?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sūtīt jums paziņojumus?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Vai atļaut lietotnei &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sūtīt jums paziņojumus ierīcē &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrolētās atļaujas"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"Lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g> ir piekļuve atrašanās vietai"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Jūsu organizācija ļauj lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g> piekļūt jūsu atrašanās vietai."</string>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nav"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Pēdējās\n24 stundās"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Pēdējo 7 dienu\nlaikā"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>: procentuālā daļa ir <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> aizsargā Android. Tā kā jūsu dati tiek apstrādāti šajā ierīcē, šīs lietotnes atļauju lietojums netiek rādīts statusa joslā vai jūsu konfidencialitātes informācijas panelī."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> aizsargā Android. Tā kā jūsu dati tiek apstrādāti šajā ierīcē, šīs lietotnes atļauju lietojums netiek rādīts jūsu konfidencialitātes informācijas panelī."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Ierīces kamera ir bloķēta"</string>
@@ -520,6 +561,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Lietotnēm un pakalpojumiem"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofona dati joprojām var tikt kopīgoti, kad zvanīsiet uz ārkārtas numuru."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Mainīt"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Piekļuve kamerai ir izslēgta"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Piekļuve mikrofonam ir izslēgta"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Piekļuve atrašanās vietai ir izslēgta"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Informatīvi izklaidējošās sistēmas lietotnēm"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Nepieciešamajām lietotnēm"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Šī lietotne ir nepieciešama"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Jūsu automašīnas ražotājs ir noteicis šo lietotni kā nepieciešamu"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Drošība un konfidencialitāte"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skenēt ierīci"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Noraidīt"</string>
@@ -585,7 +633,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>
@@ -619,4 +667,26 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Datu kopīgošanas atjauninājumi"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Dažās lietotnēs tika mainīti atrašanās vietas datu kopīgošanas veidi."</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Iestatījumi"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Pēdējā piekļuves reize: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Pēdējā piekļuves reize vakar: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Pēdējā piekļuves reize: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Jūsu vienreizējā parole ir 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ierobežots iestatījums"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Drošības apsvērumu dēļ šis iestatījums pašlaik nav pieejams."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Lietotnes piekļuve atļaujai “<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>” tika liegta"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Lietotne ir pieprasījusi piekļuvi sensitīvai atļaujai, kas var apdraudēt jūsu personas un finanšu informāciju.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Iespējams, lietotne nedarbosies pareizi bez šīs ierobežotās atļaujas. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Uzziniet, kā piešķirt piekļuvi.&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Lietotnes piekļuve noklusējuma lomai “<xliff:g id="ROLE_NAME">%1$s</xliff:g>” tika liegta"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Lietotne ir pieprasījusi piekļuvi sensitīvai atļaujai, kas var apdraudēt jūsu personas un finanšu informāciju.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Iespējams, lietotne nedarbosies pareizi bez šīm ierobežotajām atļaujām. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Uzziniet, kā piešķirt piekļuvi.&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Lietotnei tika liegta piekļuve"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Piekļuve šai atļaujai var apdraudēt jūsu personas un finanšu informāciju.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Iespējams, lietotne nedarbosies pareizi bez šīs ierobežotās atļaujas. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Uzziniet, kā piešķirt piekļuvi.&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Uzzināt vairāk"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Labi"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Atļaujas pieprasījums bloķēts"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Šī lietotne pieprasa papildu atļaujas, taču atļaujas nevar piešķirt straumēšanas sesijā. Vispirms piešķiriet attiecīgo atļauju savā tālrunī."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-mk-v34/strings.xml b/PermissionController/res/values-mk-v34/strings.xml
index e8f9afc25..d2a9cc0de 100644
--- a/PermissionController/res/values-mk-v34/strings.xml
+++ b/PermissionController/res/values-mk-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-mk-watch/strings.xml b/PermissionController/res/values-mk-watch/strings.xml
index 5d37a6175..9b39c1ce1 100644
--- a/PermissionController/res/values-mk-watch/strings.xml
+++ b/PermissionController/res/values-mk-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Не може да се смени"</string>
<string name="generic_yes" msgid="2489207724988649846">"Да"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Откажи"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Цело време"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"При користење на аплик."</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Цело време"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"При користење на аплик."</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Цело време"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"При користење на аплик."</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Цело време"</string>
</resources>
diff --git a/PermissionController/res/values-mk/strings.xml b/PermissionController/res/values-mk/strings.xml
index d8c542b76..3c51b26f9 100644
--- a/PermissionController/res/values-mk/strings.xml
+++ b/PermissionController/res/values-mk/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"дозволи"</string>
<string name="cancel" msgid="8943320028373963831">"Откажи"</string>
<string name="back" msgid="6249950659061523680">"Назад"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Дозволен"</string>
<string name="blocked" msgid="9195547604866033708">"Блокиран"</string>
<string name="on" msgid="280241003226755921">"Вклучено"</string>
@@ -34,6 +36,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 +63,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 +94,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>
@@ -114,8 +118,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Дејствата „Инсталирај/деинсталирај“ не се поддржани на Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Изберете до што може да пристапува &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; е ажурирана. Изберете до што може да пристапува апликацијава."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Откажи"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"Дозволи ограничен пристап"</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_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_with_device_name" msgid="7193042925656173271">"Пристап до <xliff:g id="PERM">%1$s</xliff:g> за апликацијава на <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"Управување со апликацијата при некористење"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Отстранува дозволи, брише привремени датотеки и запира известувања"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Отстранува дозволи, брише привремени датотеки, сопира известувања и ја архивира апликацијата"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"За да се заштитат вашите податоци, дозволите за апликацијава ќе се отстранат ако апликацијата не се користи неколку месеци."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ако апликацијата не се користи неколку месеци, заради заштита на податоците, ќе се отстранат следниве дозволи: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"За заштита на податоците, отстранети се дозволите од апликациите што не сте ги користеле неколку месеци."</string>
@@ -236,7 +242,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>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Со дозвола за управување со сите датотеки"</string>
<string name="ask_header" msgid="2633816846459944376">"Прашувај секогаш"</string>
<string name="denied_header" msgid="903209608358177654">"Без дозвола"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> на <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Прикажи уште апликации што имаат пристап до сите датотеки"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ден}one{# ден}other{# дена}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# час}one{# час}other{# часа}}"</string>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Стандардна аплик. за паричник"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Апликација за паричник"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Апликациите за паричник може да ги складираат кредитните и картичките за лојалност, клучевите за автомобилот, како и други работи за олеснување на различните форми на трансакции."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Да се постави <xliff:g id="APP_NAME">%1$s</xliff:g> како стандардна апликација за Wallet?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Не се потребни дозволи"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Стандардна апликација сега"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Не прашувај повторно"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Нека биде стандардна"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Повеќе поставки"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"За отворање линкови"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Стандардно за работа"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Стандардно за „Приватен простор“"</string>
<string name="default_app_none" msgid="9084592086808194457">"Нема"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Стандардно за системот)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Нема апликации"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до фотографиите и аудиовизуелните содржини на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до вашите контакти на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до локацијата на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до локацијата на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Да се промени пристапот до локацијата за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"Да се дозвоили &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да ги наоѓа, да се поврзува со и да ја утврдува релативната позиција на уредите во близина на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"Да се промени пристапот до локацијата на <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до приближната локација на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до вашиот календар на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да испраќа и да ги гледа SMS-пораките на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до фотографиите, аудиовизуелните содржини и датотеките на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до музиката и аудиото на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до фотографиите и видеата на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до повеќе фотографии и видеа на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да снима аудио на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да снима аудио на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Да се промени пристапот до микрофонот за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до вашата физичка активност на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да фотографира и да снима видео на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да фотографира и да снима видео на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Да се промени пристапот до камерата за &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до евиденцијата на повици на вашиот телефон на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Да се дозовли &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да упатува и да управува со телефонските повици на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до податоците од сензорите за вашите витални функции на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да пристапува до податоците од сензорите за вашите витални функции на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"За да дозволите апликацијава да пристапува до податоци од телесните сензори цело време, дури и кога не ја користите, "<annotation id="link">"одете во „Поставки“."</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_device_aware_sensors" msgid="5542771499929819675">"Да се продолжи со дозволата за пристап на &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; до податоците од телесните сензори на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Да се дозволи &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; да ви испраќа известувања на &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ништо"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Минатите\n24 часа"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Изминатите\n7 дена"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>: <xliff:g id="PERCENT">%2$d</xliff:g> процент"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> е заштитена од Android. Бидејќи вашите податоци се обработуваат на уредов, користењето на дозволата на апликацијава не е прикажано на лентата за статус или вашата контролна табла за приватност."</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Пристапот до камерата е исклучен"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Пристапот до микрофонот е исклучен"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Пристапот до локацијата е исклучен"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"За апликации за информации и забава"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"За задолжителните апликации"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Апликацијава е задолжителна"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Апликацијава ја бара производителот на вашиот автомобил"</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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Пристапено: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Пристапено вчера: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Пристапено: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Вашата еднократна лозинка е 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничена поставка"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"За ваша безбедност, поставкава е недостапна во моментов."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Барањето за пристап на апликацијата до <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> е одбиено"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апликацијата побара пристап до дозвола за чувствителни податоци што може да ја загрози безбедноста на вашите лични и финансиски податоци.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Можно е апликацијата да не функционира правилно без ограниченава дозвола. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Дознајте како да дозволите пристап&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Барањето на апликацијата да биде стандарднa <xliff:g id="ROLE_NAME">%1$s</xliff:g> е одбиено"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Апликацијата побара пристап до дозволи за чувствителни податоци што може да ја загрози безбедноста на вашите лични и финансиски податоци.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Можно е апликацијата да не функционира правилно без ограничениве дозволи. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Дознајте како да дозволите пристап&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Барањето за пристап на апликацијата е одбиено"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Пристапот до дозволава може да ја загрози безбедноста на вашите лични и финансиски податоци.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Можно е апликацијата да не функционира правилно без ограниченава дозвола. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Дознајте како да дозволите пристап&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Дознајте повеќе"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Во ред"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Барањето за дозвола е потиснато"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Апликацијава бара дополнителни дозволи, но нив не може да ги доделите во сесија за стриминг. Прво доделете ја дозволата на вашиот телефон."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
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-v34/strings.xml b/PermissionController/res/values-ml-v34/strings.xml
index 7f22bbf9c..74e8015ac 100644
--- a/PermissionController/res/values-ml-v34/strings.xml
+++ b/PermissionController/res/values-ml-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-ml-watch/strings.xml b/PermissionController/res/values-ml-watch/strings.xml
index 37ef4e919..2851731c9 100644
--- a/PermissionController/res/values-ml-watch/strings.xml
+++ b/PermissionController/res/values-ml-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"മാറ്റാനാവില്ല"</string>
<string name="generic_yes" msgid="2489207724988649846">"അതെ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"റദ്ദാക്കുക"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"എപ്പോഴും"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"എപ്പോഴും"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"എപ്പോഴും"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ആപ്പ് ഉപയോഗിക്കുമ്പോൾ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"എപ്പോഴും"</string>
</resources>
diff --git a/PermissionController/res/values-ml/strings.xml b/PermissionController/res/values-ml/strings.xml
index 81cf39cf5..04d9a8ae4 100644
--- a/PermissionController/res/values-ml/strings.xml
+++ b/PermissionController/res/values-ml/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"അനുമതികൾ"</string>
<string name="cancel" msgid="8943320028373963831">"റദ്ദാക്കുക"</string>
<string name="back" msgid="6249950659061523680">"മടങ്ങുക"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"ലഭ്യമാണ്"</string>
<string name="blocked" msgid="9195547604866033708">"ബ്ലോക്ക് ചെയ്‌തിരിക്കുന്നു"</string>
<string name="on" msgid="280241003226755921">"ഓണാണ്"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"ഇൻസ്‌റ്റാൾ/അൺഇൻസ്‌റ്റാൾ ചെയ്യുന്നതിന് Wear-ൽ പിന്തുണയില്ല."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"എന്തൊക്കെ ആക്‌സസ് ചെയ്യാനാണ് &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; അപ്‌ഡേറ്റ് ചെയ്‌തിരിക്കുന്നു. എന്തൊക്കെ ആക്‌സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കണമെന്ന് തിരഞ്ഞെടുക്കുക."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"റദ്ദാക്കുക"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"പരിമിതമായ ആക്‌സസ് അനുവദിക്കുക"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിലെ ഈ ആപ്പിനുള്ള <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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"ഉപയോഗിക്കാത്തപ്പോൾ ആപ്പ് മാനേജ് ചെയ്യൂ"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"അനുമതികൾ നീക്കം ചെയ്യുക, താൽക്കാലിക ഫയലുകൾ ഇല്ലാതാക്കുക, അറിയിപ്പുകൾ നിർത്തുക"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"അനുമതികൾ നീക്കം ചെയ്യുക, താൽക്കാലിക ഫയലുകൾ ഇല്ലാതാക്കുക, അറിയിപ്പുകൾ നിർത്തുക, ആപ്പ് ആർക്കൈവ് ചെയ്യുക"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"മാസങ്ങളായി ഈ ആപ്പ് ഉപയോഗിക്കുന്നില്ലെങ്കിൽ നിങ്ങളുടെ ഡാറ്റ സംരക്ഷിക്കുന്നതിന്, ഈ ആപ്പിനുള്ള അനുമതികൾ നീക്കം ചെയ്യുന്നതായിരിക്കും."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"കുറച്ച് മാസം ആപ്പ് ഉപയോഗിച്ചില്ലെങ്കിൽ, നിങ്ങളുടെ ഡാറ്റ സംരക്ഷിക്കുന്നതിന്, ഇനിപ്പറയുന്ന അനുമതികൾ നീക്കം ചെയ്യുന്നതായിരിക്കും: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"നിങ്ങളുടെ ഡാറ്റ സംരക്ഷിക്കുന്നതിന്, കുറച്ച് മാസങ്ങളായി ഉപയോഗിക്കാത്ത ആപ്പുകളിൽ നിന്ന് അനുമതികൾ നീക്കം ചെയ്തിരിക്കുന്നു."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"എല്ലാ ഫയലുകളും മാനേജ് ചെയ്യാൻ അനുവദിച്ചവ"</string>
<string name="ask_header" msgid="2633816846459944376">"എപ്പോഴും ചോദിക്കുക"</string>
<string name="denied_header" msgid="903209608358177654">"അനുവദിച്ചിട്ടില്ല"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിലെ <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"എല്ലാ ഫയലുകളും ആക്‌സസ് ചെയ്യാൻ കഴിയുന്ന കൂടുതൽ ആപ്പുകൾ കാണുക"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{ഒരു ദിവസം}other{# ദിവസം}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# മണിക്കൂർ}other{# മണിക്കൂർ}}"</string>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ഡിഫോൾട്ട് വാലറ്റ് ആപ്പ്"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet ആപ്പ്"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"വ്യത്യസ്ത തരത്തിലുള്ള ഇടപാടുകളിൽ സഹായിക്കുന്നതിന് Wallet ആപ്പുകൾക്ക് നിങ്ങളുടെ ക്രെഡിറ്റ്, ലോയൽറ്റി കാർഡുകൾ, കാർ കീകൾ, മറ്റ് ഇനങ്ങൾ എന്നിവ സംഭരിക്കാൻ കഴിയും."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g>, നിങ്ങളുടെ ഡിഫോൾട്ട് വാലറ്റ് ആപ്പ് ആക്കി സജ്ജീകരിക്കണോ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"അനുമതികളൊന്നും ആവശ്യമില്ല"</string>
<string name="request_role_current_default" msgid="738722892438247184">"നിലവിലെ ഡിഫോൾട്ട്"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"വീണ്ടും ആവശ്യപ്പെടരുത്"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ഡിഫോൾട്ടായി സജ്ജമാക്കൂ"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"കൂടുതൽ ഡിഫോൾട്ടുകൾ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ലിങ്കുകൾ തുറക്കൽ"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ജോലി ആവശ്യങ്ങൾക്ക് ഡിഫോൾട്ട്"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"സ്വകാര്യ സ്പേസിനായുള്ള ഡിഫോൾട്ട്"</string>
<string name="default_app_none" msgid="9084592086808194457">"ഒന്നുമില്ല"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(സിസ്‌റ്റം ഡിഫോൾട്ട്)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ആപ്പുകൾ ഒന്നുമില്ല"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ഫോട്ടോകളും മീഡിയയും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"നിങ്ങളുടെ കോണ്‍ടാക്റ്റുകള്‍ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങളുടെ കോൺടാക്‌റ്റുകൾ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിന്റെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനുള്ള ലൊക്കേഷൻ ആക്‌സസ് മാറ്റണോ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"ആപ്പ് നിങ്ങൾ ഉപയോഗിക്കാത്തപ്പോഴടക്കം, എല്ലാ സമയത്തും ഈ ആപ്പ് നിങ്ങളുടെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു. "<annotation id="link">"ക്രമീകരണത്തിൽ അനുവദിക്കുക."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"സമീപത്തെ ഉപകരണങ്ങൾ കണ്ടെത്താനും കണക്റ്റ് ചെയ്യാനും ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ സമീപ ഉപകരണങ്ങളുടെ ആപേക്ഷിക സ്ഥാനം കണ്ടെത്താനും കണക്റ്റ് ചെയ്യാനും നിർണ്ണയിക്കാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"സമീപത്തെ ഉപകരണങ്ങൾ കണ്ടെത്തി കണക്റ്റ് ചെയ്ത് ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ? "<annotation id="link">"ക്രമീകരണത്തിൽ അനുവദിക്കുക."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> എന്നതിന്റെ ലൊക്കേഷൻ ആക്‌സസ് \'ഏകദേശം\' എന്നതിൽ നിന്ന് \'കൃത്യമായത്\' എന്നതിലേക്ക് മാറ്റണോ?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിന്റെ ഏകദേശ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങളുടെ കലണ്ടർ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS സന്ദേശങ്ങൾ അയയ്ക്കാനും കാണാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ 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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ഫോട്ടോകളും മീഡിയയും ഫയലുകളും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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;ഫോട്ടോ, വീഡിയോ, സംഗീതം, ഓഡിയോ, മറ്റ് ഫയലുകൾ&lt;/b&gt; എന്നിവയിലേക്ക് &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ സംഗീതവും ഓഡിയോയും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ഫോട്ടോകളും വീഡിയോകളും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"ഈ ഉപകരണത്തിലെ കൂടുതൽ ഫോട്ടോകളും വീഡിയോകളും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ കൂടുതൽ ഫോട്ടോകളും വീഡിയോകളും ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ഓഡിയോ റെക്കോർഡ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനുള്ള മൈക്രോഫോൺ ആക്‌സസ് മാറ്റണോ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങളുടെ ശാരീരിക ആക്‌റ്റിവിറ്റി ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"ചിത്രം എടുക്കാനും വീഡിയോ റെക്കോർഡ് ചെയ്യാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ചിത്രങ്ങൾ എടുക്കാനും വീഡിയോ റെക്കോർഡ് ചെയ്യാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ചിത്രങ്ങൾ എടുക്കാനും വീഡിയോ റെക്കോർഡ് ചെയ്യാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനുള്ള ക്യാമറ ആക്‌സസ് മാറ്റണോ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങളുടെ ഫോൺ കോൾ ലോഗുകൾ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"ഫോൺ കോളുകൾ ചെയ്യാനും അവ മാനേജ് ചെയ്യാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ആപ്പിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ഫോൺ കോളുകൾ ചെയ്യാനും മാനേജ് ചെയ്യാനും &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"നിങ്ങളുടെ പ്രധാന ആരോഗ്യ വിവരങ്ങളുടെ സെൻസർ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങളുടെ പ്രധാന ആരോഗ്യ വിവര സെൻസർ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങളുടെ ആരോഗ്യ വിവര സെൻസർ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ ആപ്പ് ഉപയോഗിക്കുമ്പോൾ ബോഡി സെൻസർ ഡാറ്റ ആക്‌സസ് ചെയ്യാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കുന്നത് തുടരണോ?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; എന്നതിൽ നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാൻ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
<string name="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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ഒന്നുമില്ല"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"കഴിഞ്ഞ\n24 മണിക്കൂർ"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"കഴിഞ്ഞ\n7 ദിവസം"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ശതമാനം"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ആപ്പിന് Android സുരക്ഷയൊരുക്കുന്നു. നിങ്ങളുടെ ഡാറ്റ ഈ ഉപകരണത്തിൽ പ്രോസസ് ചെയ്യുന്നതിനാൽ, ഈ ആപ്പിന്റെ അനുമതി ഉപയോഗം നിങ്ങളുടെ സ്വകാര്യതാ ഡാഷ്ബോർഡിലെ സ്റ്റാറ്റസ് ബാറിൽ കാണിക്കില്ല."</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"ക്യാമറ ആക്സസ് ഓഫാണ്"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"മൈക്രോഫോൺ ആക്‌സസ് ഓഫാണ്"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"ലൊക്കേഷൻ ആക്‌സസ് ഓഫാണ്"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ഇൻഫോറ്റേയിൻമെന്റ് ആപ്പുകൾക്കായി"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ആവശ്യമായ ആപ്പുകൾക്കായി"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ഈ ആപ്പ് ആവശ്യമാണ്"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"കാറിൻ്റെ നിർമ്മാതാവ് ഈ ആപ്പ് ആവശ്യപ്പെടുന്നു"</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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>-ന് ആക്‌സസ് ചെയ്തു"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ഇന്നലെ <xliff:g id="TIME_DATE">%1$s</xliff:g>-ന് ആക്‌സസ് ചെയ്തു"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>-ന് ആക്‌സസ് ചെയ്തു"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"നിങ്ങളുടെ ഒറ്റത്തവണ പാസ്‌വേഡ് 132435 ആണ്"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"നിയന്ത്രിത ക്രമീകരണം"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ക്രമീകരണം നിലവിൽ ലഭ്യമല്ല."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ആപ്പിന് <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> എന്നതിലേക്കുള്ള ആക്‌സസ് നിരസിച്ചു"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ആപ്പ് സെൻസിറ്റീവ് വിവരങ്ങൾക്കുള്ള അനുമതിയിലേക്ക് ആക്‌സസ് അഭ്യർത്ഥിച്ചു, ഇത് നിങ്ങളുടെ വ്യക്തിപരവും സാമ്പത്തികവുമായ വിവരങ്ങളെ അപകടത്തിലാക്കിയേക്കാം.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ഈ നിയന്ത്രിത അനുമതിയില്ലാതെ ആപ്പ് ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ആക്സസ് എങ്ങനെ അനുവദിക്കുന്നുവെന്നറിയുക&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ആപ്പിന് ഡിഫോൾട്ട് <xliff:g id="ROLE_NAME">%1$s</xliff:g> ആകാനുള്ള ആക്‌സസ് നിരസിച്ചു"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"ആപ്പ് സെൻസിറ്റീവ് വിവരങ്ങൾക്കുള്ള അനുമതികളിലേക്ക് ആക്‌സസ് അഭ്യർത്ഥിച്ചു, ഇത് നിങ്ങളുടെ വ്യക്തിപരവും സാമ്പത്തികവുമായ വിവരങ്ങളെ അപകടത്തിലാക്കിയേക്കാം.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ഈ നിയന്ത്രിത അനുമതികളില്ലാതെ ആപ്പ് ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ആക്സസ് എങ്ങനെ അനുവദിക്കുന്നുവെന്നറിയുക&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ആപ്പിന് ആക്‌സസ് നിരസിച്ചു"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ഈ അനുമതിയിലേക്കുള്ള ആക്‌സസ് നിങ്ങളുടെ വ്യക്തിപരവും സാമ്പത്തികവുമായ വിവരങ്ങളെ അപകടത്തിലാക്കിയേക്കാം.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ഈ നിയന്ത്രിത അനുമതിയില്ലാതെ ആപ്പ് ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ആക്സസ് എങ്ങനെ അനുവദിക്കുന്നുവെന്നറിയുക&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"കൂടുതലറിയുക"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ശരി"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"അനുമതി അഭ്യർത്ഥന കാഴ്ച്ചയിൽ നിന്ന് മറച്ചു"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ഈ ആപ്പ് അധിക അനുമതികൾ അഭ്യർത്ഥിക്കുന്നു, എന്നാൽ സ്‌ട്രീമിംഗ് സെഷനിടയിൽ അനുമതികൾ നൽകാനാകില്ല. ആദ്യം നിങ്ങളുടെ ഫോണിൽ അനുമതി നൽകുക."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-mn-v34/strings.xml b/PermissionController/res/values-mn-v34/strings.xml
index fc5ac1818..d8c3fa504 100644
--- a/PermissionController/res/values-mn-v34/strings.xml
+++ b/PermissionController/res/values-mn-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-mn-watch/strings.xml b/PermissionController/res/values-mn-watch/strings.xml
index 689766eda..cf4bdf502 100644
--- a/PermissionController/res/values-mn-watch/strings.xml
+++ b/PermissionController/res/values-mn-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Өөрчлөх боломжгүй"</string>
<string name="generic_yes" msgid="2489207724988649846">"Тийм"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Цуцлах"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Үргэлж"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Аппыг ашиглаж байх үед"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Үргэлж"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Аппыг ашиглаж байх үед"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Үргэлж"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Аппыг ашиглаж байх үед"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Үргэлж"</string>
</resources>
diff --git a/PermissionController/res/values-mn/strings.xml b/PermissionController/res/values-mn/strings.xml
index 3df06591c..a55e61d1f 100644
--- a/PermissionController/res/values-mn/strings.xml
+++ b/PermissionController/res/values-mn/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"зөвшөөрөл"</string>
<string name="cancel" msgid="8943320028373963831">"Болих"</string>
<string name="back" msgid="6249950659061523680">"Буцах"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Боломжтой"</string>
<string name="blocked" msgid="9195547604866033708">"Блоклосон"</string>
<string name="on" msgid="280241003226755921">"Асаалттай"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<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">"Андройд Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear-д суулгах/устгах үйлдлийг дэмждэггүй."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-н хандаж болох зүйлсийг сонгоно уу"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-г шинэчилсэн. Энэ аппын хандаж болох зүйлсийг сонгоно уу."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Цуцлах"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"Хязгаарлагдмал хандалтыг зөвшөөрөх"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> дээрх энэ аппын <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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"Хэрэв ашиглаагүй бол аппыг удирдах"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Зөвшөөрлийг хасаж, түр зуурын файлыг устгаж мөн мэдэгдлийг зогсооно"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Зөвшөөрлийг хасаж, түр зуурын файлыг устгаж, мэдэгдлийг зогсоож мөн аппыг архивлана"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Таны өгөгдлийг хамгаалах үүднээс энэ аппыг хэдэн сарын турш ашиглахгүй бол зөвшөөрлийг нь хасах болно."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Таны өгөгдлийг хамгаалах үүднээс аппыг хэдэн сарын турш ашиглахгүй бол дараах зөвшөөрлүүдийг хасах болно: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Таны өгөгдлийг хамгаалах үүднээс таны хэдэн сарын турш ашиглаагүй аппуудын зөвшөөрлийг нь хассан."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Бүх файлыг удирдахыг зөвшөөрсөн"</string>
<string name="ask_header" msgid="2633816846459944376">"Тухай бүрд асуух"</string>
<string name="denied_header" msgid="903209608358177654">"Зөвшөөрөөгүй"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> дээрх <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Бүх файлд хандах боломжтой бусад аппыг харах"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 өдөр}other{# өдөр}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# цаг}other{# цаг}}"</string>
@@ -374,7 +381,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>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Өгөгдмөл түрийвчийн апп"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Түрийвчийн апп"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Төрөл бүрийн гүйлгээнд туслахын тулд түрийвчийн аппууд таны кредит ба лояалти карт, машины түлхүүр болон бусад зүйлийг хадгалах боломжтой."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г таны өгөгдмөл түрийвчийн аппаар тохируулах уу?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Ямар ч зөвшөөрөл шаардлагагүй"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Одоогийн өгөгдмөл апп"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Дахиж бүү асуу"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Өгөгдмөлөөр тохируулах"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Бусад өгөгдмөл апп"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Холбоосыг нээх сонголт"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Ажлын өгөгдмөл апп"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Хаалттай орон зайн өгөгдмөл"</string>
<string name="default_app_none" msgid="9084592086808194457">"Тохируулсан апп алга"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Системийн өгөгдмөл)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Апп алга"</string>
@@ -443,7 +456,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>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;-н байршилд хандахыг зөвшөөрөх үү?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Энэ апп нь таныг апп ашиглаагүй байх үед ч таны байршилд үргэлж хандах хүcэлтэй байж болзошгүй. "<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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-н &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; дээрх байршлын хандалтыг өөрчлөх үү?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Энэ апп нь таныг апп ашиглаагүй байх үед ч таны байршилд үргэлж хандах хүcэлтэй байна. "<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_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>-н &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-н &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-н &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; дээрх таны биеийн ерөнхий үзүүлэлтийн мэдрэгчийн өгөгдөлд хандахыг зөвшөөрөх үү?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Та аппыг ашиглаагүй байсан ч ямар ч үед энэ аппын биеийн мэдрэгчийн өгөгдөлд хандахыг зөвшөөрөх бол "<annotation id="link">"тохиргоо руу очно уу."</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_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-г ашиглаж байх үед тус аппад &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;-д &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Хоосон"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Өнгөрсөн\n24 цагт"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Өнгөрсөн\n7 хоног"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> хувь"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь Android-р хамгаалагдсан. Таны өгөгдлийг энэ төхөөрөмж дээр боловсруулдаг тул энэ аппын зөвшөөрлийн ашиглалтыг статус самбар эсвэл таны нууцлалын хяналтын самбарт харуулаагүй."</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Камерын хандалт унтраалттай байна"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Микрофоны хандалт унтраалттай байна"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Байршлын хандалт унтраалттай байна"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Инфотэйнмент аппуудад зориулсан"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Шаардлагатай аппуудад зориулсан"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Энэ апп шаардлагатай"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Энэ аппыг танай машин үйлдвэрлэгчээс шаарддаг"</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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>-д хандсан"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Өчигдөр <xliff:g id="TIME_DATE">%1$s</xliff:g>-д хандсан"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>-д хандсан"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Таны нэг удаагийн нууц үг бол 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Хязгаарлагдсан тохиргоо"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Таны аюулгүй байдлын үүднээс энэ тохиргоо одоогоор боломжгүй байна."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Аппад <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>-д хандах эрх олгохоос татгалзсан"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апп нь таны хувийн болон санхүүгийн мэдээллийг эрсдэлд оруулж болох эмзэг зөвшөөрөлд хандах эрх хүссэн.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Энэ хязгаарлагдмал зөвшөөрөлгүйгээр уг апп зохих ёсоор ажиллахгүй байх боломжтой. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Хандах эрхийг хэрхэн зөвшөөрөх талаар мэдэж авах&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Аппад өгөгдмөл <xliff:g id="ROLE_NAME">%1$s</xliff:g> болох эрх олгохоос татгалзсан"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Апп нь таны хувийн болон санхүүгийн мэдээллийг эрсдэлд оруулж болох эмзэг зөвшөөрлүүдэд хандах эрх хүссэн.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Эдгээр хязгаарлагдмал зөвшөөрөлгүйгээр уг апп зохих ёсоор ажиллахгүй байх боломжтой. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Хандах эрхийг хэрхэн зөвшөөрөх талаар мэдэж авах&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Аппад хандах эрх олгохоос татгалзсан"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Энэ зөвшөөрөлд хандах эрх нь таны хувийн болон санхүүгийн мэдээллийг эрсдэлд оруулж болно.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Энэ хязгаарлагдмал зөвшөөрөлгүйгээр уг апп зохих ёсоор ажиллахгүй байх боломжтой. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Хандах эрхийг хэрхэн зөвшөөрөх талаар мэдэж авах&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Нэмэлт мэдээлэл авах"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ОК"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Зөвшөөрлийн хүсэлтийг зогсоосон"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Энэ апп нэмэлт зөвшөөрөл хүсэж байгаа ч дамжуулалтын харилцан үйлдлийн үеэр зөвшөөрөл олгох боломжгүй. Эхлээд утсан дээрээ зөвшөөрөл олгоно уу."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-mr-v34/strings.xml b/PermissionController/res/values-mr-v34/strings.xml
index 8c9152379..e130cff40 100644
--- a/PermissionController/res/values-mr-v34/strings.xml
+++ b/PermissionController/res/values-mr-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-mr-watch/strings.xml b/PermissionController/res/values-mr-watch/strings.xml
index 474ea9d82..77d7ee44a 100644
--- a/PermissionController/res/values-mr-watch/strings.xml
+++ b/PermissionController/res/values-mr-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"बदलू शकत नाही"</string>
<string name="generic_yes" msgid="2489207724988649846">"होय"</string>
<string name="generic_cancel" msgid="2631708607129269698">"रद्द करा"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"पूर्ण वेळ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"अ‍ॅप वापरताना"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"पूर्ण वेळ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"अ‍ॅप वापरताना"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"पूर्ण वेळ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"अ‍ॅप वापरताना"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"पूर्ण वेळ"</string>
</resources>
diff --git a/PermissionController/res/values-mr/strings.xml b/PermissionController/res/values-mr/strings.xml
index d46792c9f..2ecdcc01e 100644
--- a/PermissionController/res/values-mr/strings.xml
+++ b/PermissionController/res/values-mr/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"परवानग्या"</string>
<string name="cancel" msgid="8943320028373963831">"रद्द करा"</string>
<string name="back" msgid="6249950659061523680">"मागे जा"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"बंद करा"</string>
<string name="available" msgid="6007778121920339498">"उपलब्ध आहे"</string>
<string name="blocked" msgid="9195547604866033708">"ब्लॉक केला आहे"</string>
<string name="on" msgid="280241003226755921">"सुरू आहे"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"इंस्टॉल करा/अनइंस्टॉल करा क्रिया Wear वर सपोर्ट करत नाहीत."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला काय अ‍ॅक्सेस करण्‍याची परवानगी द्यावी ते निवडा"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; अपडेट केले गेले आहे. या ॲपला काय अ‍ॅक्सेस करण्‍याची परवानगी द्यावी ते निवडा."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"रद्द करा"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"मर्यादित अ‍ॅक्सेसची अनुमती द्या"</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_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_with_device_name" msgid="7193042925656173271">"या अ‍ॅपसाठी <xliff:g id="DEVICE_NAME">%2$s</xliff:g> वरील <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"वापरले नसल्यास ॲप व्यवस्थापित करा"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"परवानग्या काढून टाका, तात्पुरत्या फाइल हटवा आणि सूचना थांबवा"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"परवानग्या काढून टाका, तात्पुरत्या फाइल हटवा, सूचना थांबवा आणि ॲप संग्रहित करा"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"तुमच्या डेटाचे संरक्षण करण्यासाठी, अ‍ॅप काही महिन्यांत वापरले गेले नसल्यास, या अ‍ॅपच्या परवानग्या काढल्या जातील."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"तुमच्या डेटाचे संरक्षण करण्यासाठी, अ‍ॅप काही महिन्यांत वापरले गेले नसल्यास, पुढील परवानग्या काढल्या जातील: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"तुमच्या डेटाचे संरक्षण करण्यासाठी, तुम्ही काही महिन्यांत न वापरलेल्या ॲप्समधून परवानग्या काढल्या गेल्या आहेत."</string>
@@ -219,7 +224,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"सर्व फाइल व्यवस्थापित करण्यास अनुमती दिली आहे"</string>
<string name="ask_header" msgid="2633816846459944376">"प्रत्येक वेळी विचारा"</string>
<string name="denied_header" msgid="903209608358177654">"अनुमती नाही"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> वरील <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"सर्व फाइलचा ॲक्सेस असलेली आणखी ॲप्स पहा"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{एक दिवस}other{# दिवस}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# तास}other{# तास}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"डीफॉल्ट वॉलेट अ‍ॅप"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"वॉलेट अ‍ॅप"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"वॉलेट ॲप्स ही तुमची क्रेडिट आणि लॉयल्टी कार्ड, कार की व विविध प्रकारच्या व्यवहारांमध्ये मदत करण्यासाठी इतर गोष्टी स्टोअर करू शकतात."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमचे डीफॉल्ट वॉलेट ॲप म्हणून सेट करायचे आहे का?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"कोणत्याही परवानगीची आवश्यकता नाही"</string>
<string name="request_role_current_default" msgid="738722892438247184">"सद्य डीफॉल्ट"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"पुन्हा विचारू नका"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"डीफॉल्ट सेट करा"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"आणखी डीफॉल्ट"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"उघडणार्‍या लिंक"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"कार्यासाठी डीफॉल्ट"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"खाजगी स्पेससाठी डीफॉल्ट"</string>
<string name="default_app_none" msgid="9084592086808194457">"काहीही नाही"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डीफॉल्ट)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"अ‍ॅप्स नाहीत"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; वर &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; चा स्थान अ‍ॅक्सेस बदलायचा आहे का?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"तुम्ही अ‍ॅप वापरत नसतानादेखील या ॲपला नेहमी तुमचे स्थान ॲक्सेस करायचे आहे. "<annotation id="link">"सेटिंग्जमधून अनुमती द्या."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला जवळील डिव्हाइस शोधण्याची, त्यांच्याशी कनेक्ट व त्यांचे संबंधित स्थान निर्धारित करण्याची अनुमती द्यायची का?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; वर <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; वर &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; चा मायक्रोफोन अ‍ॅक्सेस बदलायचा आहे का?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; वर &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; चा कॅमेरा अ‍ॅक्सेस बदलायचा आहे का?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; वर तुमच्या महत्त्वाच्या लक्षणांबद्दलचा सेन्सर डेटा अ‍ॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"तुम्ही ॲप वापरत नसतानादेखील या ॲपला शरीर सेन्सर डेटा नेहमी अ‍ॅक्सेस करू देण्यासाठी, "<annotation id="link">"सेटिंग्जवर जा."</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_device_aware_sensors" msgid="5542771499929819675">"अ‍ॅप अजूनही वापरात असताना &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"कोणतेही नाही"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"मागील\n२४ तास"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"मागील\nसात दिवसातील"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> टक्के"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे Android द्वारे सुरक्षित आहे. तुमच्या या डिव्हाइसवरील डेटावर प्रक्रिया केली जात असल्यामुळे या अ‍ॅपच्या परवानगीचा वापर तुमच्या गोपनीयता डॅशबोर्डवरील स्टेटस बारवर दाखवण्यात आलेला नाही."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"कॅमेराचा ॲक्सेस बंद आहे"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"मायक्रोफोनचा अ‍ॅक्सेस बंद आहे"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"स्थानाचा अ‍ॅक्सेस बंद आहे"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"इन्फोटेनमेंट ॲप्ससाठी"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"आवश्यक अ‍ॅप्ससाठी"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"हे ॲप आवश्यक आहे"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"हे ॲप तुमच्या कारच्या निर्मात्यासाठी आवश्यक आहे"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> वाजता ॲक्सेस केले"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"काल <xliff:g id="TIME_DATE">%1$s</xliff:g> वाजता ॲक्सेस केले"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> ला <xliff:g id="TIME_DATE_1">%2$s</xliff:g> वाजता ॲक्सेस केले"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"तुमचा वन टाइम पासवर्ड १३२४३५ हा आहे"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"प्रतिबंधित सेटिंग"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"तुमच्या सुरक्षेसाठी, हे सेटिंग सध्या उपलब्ध नाही."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"अ‍ॅपचा <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> साठीचा अ‍ॅक्सेस नाकारला गेला आहे"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ॲपने संवेदनशील परवानगीसाठी अ‍ॅक्सेसची विनंती केली आहे, ज्यामुळे तुमची वैयक्तिक आणि आर्थिक माहिती धोक्यात येऊ शकते.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>या प्रतिबंधित परवानगीशिवाय ॲप कदाचित योग्यरीत्या काम करणार नाही. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अ‍ॅक्सेसला अनुमती कशी द्यावी हे जाणून घ्या&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"अ‍ॅपचा डीफॉल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> असण्यासाठीचा अ‍ॅक्सेस नाकारला गेला आहे"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"ॲपने संवेदनशील परवानग्यांसाठी अ‍ॅक्सेसची विनंती केली आहे, ज्यामुळे तुमची वैयक्तिक आणि आर्थिक माहिती धोक्यात येऊ शकते.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>या प्रतिबंधित परवानग्यांशिवाय ॲप कदाचित योग्यरीत्या काम करणार नाही. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अ‍ॅक्सेसला अनुमती कशी द्यावी हे जाणून घ्या&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"अ‍ॅपचा अ‍ॅक्सेस नाकारला गेला आहे"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"या परवानगीच्या अ‍ॅक्सेसमुळे तुमची वैयक्तिक आणि आर्थिक माहिती धोक्यात येऊ शकते.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>या प्रतिबंधित परवानगीशिवाय ॲप कदाचित योग्यरित्या काम करणार नाही. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अ‍ॅक्सेसला अनुमती कशी द्यावी हे जाणून घ्या&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"अधिक जाणून घ्या"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ओके"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"परवानगी मागणारी विनंती सप्रेस केली आहे"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"हे ॲप अतिरिक्त परवानग्यांची विनंती करत आहे, पण स्ट्रीमिंग सेशनदरम्यान परवानग्या दिल्या जाऊ शकत नाहीत. आधी तुमच्या फोनवर परवानगी द्या."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"आणीबाणी कॉल करण्यासाठी किंवा एसएमएस पाठवण्यासाठी"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"आणीबाणी सेवांना स्थान पाठवले आहे"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"आणीबाणी नंबरवर कॉल करत असताना किंवा एसएमएस पाठवत असताना या ॲपने तुमच्या डिव्हाइसचे स्थान ॲक्सेस केले आहे. ॲपकडे स्थान परवानगी नसताना किंवा डिव्हाइसचे स्थान बंद असतानादेखील हे होऊ शकते. "<a href="https://support.google.com/android/answer/9319337">"अधिक जाणून घ्या"</a></string>
</resources>
diff --git a/PermissionController/res/values-ms-v34/strings.xml b/PermissionController/res/values-ms-v34/strings.xml
index 6fd21c7af..b544d95d0 100644
--- a/PermissionController/res/values-ms-v34/strings.xml
+++ b/PermissionController/res/values-ms-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Urus akses apl kepada data kesihatan"</string>
<string name="location_settings" msgid="8863940440881290182">"Akses lokasi"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Untuk apl dan perkhidmatan. Jika tetapan ini dimatikan, data mikrofon mungkin masih dikongsi apabila anda memanggil nombor kecemasan"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Untuk apl dan perkhidmatan"</string>
</resources>
diff --git a/PermissionController/res/values-ms-watch/strings.xml b/PermissionController/res/values-ms-watch/strings.xml
index f762231d0..9791f6df8 100644
--- a/PermissionController/res/values-ms-watch/strings.xml
+++ b/PermissionController/res/values-ms-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Tidak dpt diubah"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ya"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Batal"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Sepanjang masa"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Semasa menggunakan apl"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Sepanjang masa"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Semasa menggunakan apl"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Sepanjang masa"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Semasa menggunakan apl"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Sepanjang masa"</string>
</resources>
diff --git a/PermissionController/res/values-ms/strings.xml b/PermissionController/res/values-ms/strings.xml
index 7e560fa73..5d78c16eb 100644
--- a/PermissionController/res/values-ms/strings.xml
+++ b/PermissionController/res/values-ms/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"kebenaran"</string>
<string name="cancel" msgid="8943320028373963831">"Batal"</string>
<string name="back" msgid="6249950659061523680">"Kembali"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Tutup"</string>
<string name="available" msgid="6007778121920339498">"Tersedia"</string>
<string name="blocked" msgid="9195547604866033708">"Disekat"</string>
<string name="on" msgid="280241003226755921">"Hidup"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Semua kebenaran"</string>
<string name="other_permissions" msgid="2901186127193849594">"Keupayaan apl lain"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Permintaan kebenaran"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Tindakan pasang/nyahpasang tidak disokong pada Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Pilih perkara yang boleh diakses oleh &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; telah dikemas kini. Pilih perkara yang boleh diakses oleh apl ini."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Batal"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Sentiasa benarkan semua"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Tanya setiap kali"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Jangan benarkan"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Benarkan akses terhad"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Lokasi tepat"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Lokasi anggaran"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Gunakan lokasi tepat"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Apabila lokasi tepat dimatikan, apl boleh mengakses lokasi anggaran anda"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Kebenaran <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Akses <xliff:g id="PERM">%1$s</xliff:g> untuk apl ini"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"akses <xliff:g id="PERM">%1$s</xliff:g> untuk apl ini pada <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Lihat semua kebenaran <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Lihat semua apl dengan kebenaran ini"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Tunjukkan penggunaan mikrofon pembantu"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Alih keluar kebenaran jika apl tidak digunakan"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Alih keluar kebenaran dan kosongkan ruang"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Jeda aktiviti apl jika tidak digunakan"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Urus apl jika tidak digunakan"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Alih keluar kebenaran, padamkan fail sementara dan hentikan pemberitahuan"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Alih keluar kebenaran, padamkan fail sementara, hentikan pemberitahuan dan arkibkan apl tersebut"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Untuk melindungi data anda, kebenaran apl ini akan dialih keluar jika apl tidak digunakan selama beberapa bulan."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Untuk melindungi data anda, jika apl tidak digunakan selama beberapa bulan, kebenaran berikut akan dialih keluar: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Untuk melindungi data anda, kebenaran telah dialih keluar daripada apl yang tidak anda gunakan selama beberapa bulan."</string>
@@ -247,11 +252,12 @@
<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>
<string name="denied_header" msgid="903209608358177654">"Tidak dibenarkan"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> pada <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Lihat lagi apl yang boleh mengakses semua fail"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 hari}other{# hari}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# jam}other{# jam}}"</string>
@@ -375,7 +381,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Apl nota"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apl yang membolehkan anda mengambil nota pada peranti anda"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"nota"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Apl dompet lalai"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Apl dompet"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Apl dompet boleh menyimpan kad kredit dan kad kesetiaan, kunci kereta dan item lain untuk memudahkan pelbagai bentuk transaksi."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Tetapkan <xliff:g id="APP_NAME">%1$s</xliff:g> sebagai apl dompet lalai anda?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Kebenaran tidak diperlukan"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Lalai semasa"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Jangan tanya lagi"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Tetapkan sbg lalai"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Lagi tetapan lalai"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Membuka pautan"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Lalai untuk kerja"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Lalai untuk ruang privasi"</string>
<string name="default_app_none" msgid="9084592086808194457">"Tiada"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Ciri lalai sistem)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Tiada apl"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Tunjukkan pengesanan cetusan pembantu"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Tunjukkan ikon dalam bar status apabila mikrofon digunakan untuk mengaktifkan pembantu suara"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan media pada peranti anda?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan media pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kenalan anda?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kenalan anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi peranti ini?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Apl ini hanya dapat mengakses lokasi semasa anda menggunakan apl tersebut"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi peranti ini?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Apl ini mungkin mahu mengakses lokasi anda pada sepanjang masa, meskipun apabila anda tidak menggunakan apl itu. "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Tukar akses lokasi untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Tukar akses lokasi untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Apl ini mahu mengakses lokasi anda pada sepanjang masa, meskipun apabila anda tidak menggunakan apl itu. "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; untuk mencari, menyambung kepada dan menentukan penempatan relatif peranti berdekatan?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menemukan, menyambung kepada &amp; menentukan penempatan relatif peranti berdekatan pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; untuk mencari, menyambung kepada dan menentukan penempatan relatif peranti berdekatan? "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Tukar akses lokasi <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> daripada anggaran kepada tepat?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Tukar akses lokasi <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; daripada lokasi anggaran kepada lokasi tepat?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi anggaran peranti ini?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lokasi anggaran &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Tepat"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Anggaran"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kalendar anda?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses kalendar anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menghantar dan melihat mesej SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menghantar dan melihat mesej SMS pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto, media dan fail pada peranti anda?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto, media dan fail pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses &lt;b&gt;foto, video, muzik dan audio&lt;/b&gt; pada peranti ini?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses &lt;b&gt;foto, video, muzik, audio dan fail lain&lt;/b&gt; pada peranti ini?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses muzik dan audio pada peranti ini?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses muzik dan audio pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video pada peranti ini?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses foto dan video pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lebih banyak foto dan video pada peranti ini?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses lebih banyak foto dan video pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Apl hanya boleh merakam audio semasa anda menggunakan apl tersebut"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; merakam audio pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Apl ini mungkin mahu merakam audio pada sepanjang masa, meskipun apabila anda tidak menggunakan apl itu. "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Tukar akses mikrofon untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Tukar akses mikrofon untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Apl ini mahu merakam audio pada sepanjang masa, meskipun apabila anda tidak menggunakan apl itu. "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses aktiviti fizikal anda?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses aktiviti fizikal anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merakam video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merakam video pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Apl hanya boleh mengambil gambar dan merakam video semasa anda menggunakan apl tersebut"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merakam video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengambil gambar dan merakam video pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Apl ini mungkin mahu mengambil gambar dan merakam video pada sepanjang masa, meskipun apabila anda tidak menggunakan apl itu. "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Tukar akses kamera untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Tukar akses kamera untuk &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Apl ini mahu mengambil gambar dan merakam video pada sepanjang masa, meskipun apabila anda tidak menggunakan apl itu. "<annotation id="link">"Benarkan dalam tetapan."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses log panggilan telefon anda?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses log panggilan telefon anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; membuat dan mengurus panggilan telefon?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; membuat dan mengurus panggilan telefon pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data penderia tentang tanda vital anda?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data penderia tentang tanda vital anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Apl ini mahu mengakses data penderia tentang tanda vital anda pada setiap masa, meskipun apabila anda tidak menggunakan apl. Untuk membuat perubahan ini, "<annotation id="link">"pergi ke tetapan."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data penderia tentang tanda vital anda?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data penderia tentang tanda vital anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Untuk membenarkan apl ini mengakses data penderia tubuh pada setiap masa, termasuk apabila anda tidak menggunakan apl, "<annotation id="link">"pergi ke tetapan."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Terus membenarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data penderia tubuh semasa apl digunakan?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Terus membenarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; mengakses data penderia tubuh pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; semasa apl digunakan?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menghantar pemberitahuan kepada anda?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Benarkan &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; menghantar pemberitahuan kepada anda pada &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kebenaran terkawal"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> memiliki akses lokasi"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organisasi anda membenarkan <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengakses lokasi anda"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Tiada"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 jam\nyang lalu"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 hari\nyang lalu"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> peratus"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Ini ialah <xliff:g id="APP_NAME">%1$s</xliff:g> Protected by Android. Oleh sebab data anda diproses pada peranti ini, penggunaan kebenaran apl ini tidak ditunjukkan pada bar status atau papan pemuka privasi anda."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Ini ialah <xliff:g id="APP_NAME">%1$s</xliff:g> Protected by Android. Oleh sebab data anda diproses pada peranti ini, penggunaan kebenaran apl ini tidak ditunjukkan pada papan pemuka privasi anda."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera peranti disekat"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Untuk apl dan perkhidmatan"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Data mikrofon mungkin masih dikongsi apabila anda memanggil nombor kecemasan."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Tukar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Akses kamera dimatikan"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Akses mikrofon dimatikan"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Akses lokasi dimatikan"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Untuk apl maklumat hibur"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Untuk apl yang diperlukan"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Apl ini diperlukan"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Apl ini diperlukan oleh pengilang kereta anda"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Keselamatan &amp; privasi"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Imbas peranti"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ketepikan"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Kemaskinian perkongsian data"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Sesetengah apl mengubah cara apl itu boleh berkongsi data lokasi anda"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Tetapan"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Diakses <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Diakses semalam <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Diakses <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Kata laluan sekali guna anda ialah 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Tetapan terhad"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Untuk keselamatan anda, tetapan ini tidak tersedia pada masa ini."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Akses apl kepada <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> telah ditolak"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Akses apl untuk menjadi <xliff:g id="ROLE_NAME">%1$s</xliff:g> secara lalai telah ditolak"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Apl meminta akses kepada kebenaran sensitif yang boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Akses apl telah ditolak"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Akses kepada kebenaran ini boleh mengakibatkan risiko terhadap maklumat peribadi dan kewangan anda.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Apl tersebut mungkin tidak dapat berfungsi dengan betul tanpa kebenaran terhad ini. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ketahui cara membenarkan akses&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Ketahui lebih lanjut"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Permintaan kebenaran disekat"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Apl ini meminta kebenaran tambahan tetapi kebenaran tidak boleh diberikan dalam sesi penstriman. Berikan kebenaran pada telefon anda dahulu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Untuk panggilan atau teks kecemasan"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokasi dihantar kepada perkhidmatan kecemasan"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Apl ini mengakses lokasi peranti anda semasa panggilan atau teks kepada nombor kecemasan. Hal ini boleh berlaku apabila apl tiada kebenaran lokasi atau lokasi peranti telah dimatikan. "<a href="https://support.google.com/android/answer/9319337">"Ketahui lebih lanjut"</a></string>
</resources>
diff --git a/PermissionController/res/values-my-v34/strings.xml b/PermissionController/res/values-my-v34/strings.xml
index 4d4e03401..102d3bd4c 100644
--- a/PermissionController/res/values-my-v34/strings.xml
+++ b/PermissionController/res/values-my-v34/strings.xml
@@ -17,11 +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="location_settings" msgid="8863940440881290182">"တည်နေရာသုံးခွင့်"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"အက်ပ်နှင့် ဝန်ဆောင်မှုများအတွက်။ ဤဆက်တင်ကို ပိတ်ထားသော်လည်း အရေးပေါ် နံပါတ်ကို သင်ခေါ်ဆိုချိန်တွင် မိုက်ခရိုဖုန်းဒေတာ မျှဝေနိုင်သေးသည်"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"အက်ပ်နှင့် ဝန်ဆောင်မှုများအတွက်"</string>
</resources>
diff --git a/PermissionController/res/values-my-watch/strings.xml b/PermissionController/res/values-my-watch/strings.xml
index 6486ae7ff..cd9159fa6 100644
--- a/PermissionController/res/values-my-watch/strings.xml
+++ b/PermissionController/res/values-my-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ပြောင်းလဲ မရနိုင်ပါ"</string>
<string name="generic_yes" msgid="2489207724988649846">"Yes"</string>
<string name="generic_cancel" msgid="2631708607129269698">"မလုပ်တော့"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"အချိန်တိုင်း"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"အက်ပ်ကို သုံးနေစဉ်"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"အချိန်တိုင်း"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"အက်ပ်ကို သုံးနေစဉ်"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"အချိန်တိုင်း"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"အက်ပ်ကို သုံးနေစဉ်"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"အချိန်တိုင်း"</string>
</resources>
diff --git a/PermissionController/res/values-my/strings.xml b/PermissionController/res/values-my/strings.xml
index 6bedeab18..71f620ef0 100644
--- a/PermissionController/res/values-my/strings.xml
+++ b/PermissionController/res/values-my/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ခွင့်ပြုချက်များ"</string>
<string name="cancel" msgid="8943320028373963831">"မလုပ်တော့"</string>
<string name="back" msgid="6249950659061523680">"နောက်သို့"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"ပိတ်ရန်"</string>
<string name="available" msgid="6007778121920339498">"ရနိုင်သည်"</string>
<string name="blocked" msgid="9195547604866033708">"ပိတ်ထားသည်"</string>
<string name="on" msgid="280241003226755921">"ဖွင့်"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear ပေါ်တွင် ထည့်သွင်းခြင်း/ဖြုတ်ခြင်းများကို ပံ့ပိုးမထားပါ။"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&amp;It;b7gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;It;/b&gt; က အသုံးပြုခွင့်ရမည့် အရာတို့ကို ရွေးပါ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&amp;It;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&amp;It;/b&gt; ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။ ဤအက်ပ်က အသုံးပြုခွင့်ရမည့်အရာတို့ကို ရွေးပါ။"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"မလုပ်တော့"</string>
@@ -124,7 +125,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>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"ကန့်သတ်သုံးခြင်းကို ခွင့်ပြုရန်"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> တွင် ဤအက်ပ်အတွက် <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"အသုံးမပြုပါက အက်ပ်ကို စီမံရန်"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ခွင့်ပြုချက် ဖယ်ရှားခြင်း၊ ယာယီဖိုင် ဖျက်ခြင်း၊ အကြောင်းကြားချက် ရပ်ခြင်း"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ခွင့်ပြုချက်များ ဖယ်ရှားခြင်း၊ ယာယီဖိုင်များ ဖျက်ခြင်း၊ အကြောင်းကြားချက်များ ရပ်ခြင်းနှင့် အက်ပ်သိမ်းခြင်းတို့ ပြုလုပ်နိုင်သည်"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"အက်ပ်ကို လအနည်းငယ် အသုံးမပြုပါက သင်၏ဒေတာကိုကာကွယ်ရန် ဤအက်ပ်အတွက် ခွင့်ပြုချက်များကို ဖယ်ရှားပါမည်။"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"အက်ပ်ကို လအနည်းငယ် အသုံးမပြုပါက သင်၏ဒေတာကိုကာကွယ်ရန် အောက်ပါခွင့်ပြုချက်များကို ဖယ်ရှားပါမည်- <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"သင်၏ဒေတာကိုကာကွယ်ရန် လအနည်းငယ်အတွင်း အသုံးမပြုသော အက်ပ်များမှ ခွင့်ပြုချက်များကို ဖယ်ရှားလိုက်သည်။"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ဖိုင်အားလုံးကို စီမံခွင့်ပြုထားသည်"</string>
<string name="ask_header" msgid="2633816846459944376">"အမြဲမေးရန်"</string>
<string name="denied_header" msgid="903209608358177654">"ခွင့်ပြုမထားပါ"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ရှိ <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ဖိုင်အားလုံးသုံးနိုင်သည့် နောက်ထပ်အက်ပ်များ ကြည့်ရန်"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ရက်}other{# ရက်}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# နာရီ}other{# နာရီ}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"မူရင်း Wallet အက်ပ်"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet အက်ပ်"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Wallet အက်ပ်များသည် ငွေလွှဲပြောင်းမှု ပုံစံအမျိုးမျိုးအတွက် ကူညီရန် သင်၏ခရက်ဒစ်ကတ်၊ ဖောက်သည်ကတ်များ၊ ကားသော့နှင့် အခြားအရာများကို သိမ်းနိုင်သည်။"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို သင်၏မူရင်း Wallet အက်ပ်အဖြစ် သတ်မှတ်မလား။"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ခွင့်ပြုချက် မလိုပါ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"လက်ရှိ မူရင်း"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ထပ်မမေးပါနှင့်"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"မူရင်း သတ်မှတ်ရန်"</string>
@@ -426,13 +437,14 @@
<string name="default_apps_more" msgid="4078194675848858093">"နောက်ထပ် မူရင်းများ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"လင့်ခ်များကို ဖွင့်ခြင်း"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"အလုပ်အတွက် မူရင်း"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"သီးသန့်နေရာအတွက် မူလအက်ပ်များ"</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>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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&amp;gt တို့အတွက် တည်နေရာ ဝင်ခွင့် ပြောင်းပေးမလား။"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; တွင် &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အတွက် တည်နေရာရယူခွင့် ပြောင်းမလား။"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"သင် အသုံးမပြုနေလျှင်တောင်မှ ဤအက်ပ်က သင့်တည်နေရာကို သုံးခွင့်ရလိုသည်။ "<annotation id="link">"ဆက်တင်များတွင် ခွင့်ပြုပါ။"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"အနီးရှိ စက်များ၏ မှန်းခြေနေရာ ရှာရန်၊ ချိတ်ဆက်ရန်နှင့် သတ်မှတ်ရန် &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို ခွင့်ပြုမလား။"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; တွင် <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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;ဓာတ်ပုံ၊ ဗီဒီယို၊ တေးဂီတ၊ အသံနှင့်အခြားဖိုင်များ&lt;/b&gt; ကို &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; တွင် &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အတွက် မိုက်ခရိုဖုန်းသုံးခွင့် ပြောင်းမလား။"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; တွင် &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အတွက် ကင်မရာသုံးခွင့် ပြောင်းမလား။"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို to &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အား &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ရှိ သင့်အဓိကကိုယ်တွင်းအင်္ဂါအခြေအနေ လက္ခဏာများနှင့်ပတ်သက်သည့် အာရုံခံစနစ် ဒေတာကို ရယူခွင့်ပြုမလား။"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"အက်ပ်သုံးမနေသော်လည်း ဤအက်ပ်ကို ခန္ဓာကိုယ်အာရုံခံစနစ် ဒေတာများ အမြဲသုံးခွင့်ပြုရန် "<annotation id="link">"ဆက်တင်များသို့ သွားပါ။"</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_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; အက်ပ် အသုံးပြုနေစဉ် &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ကို &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"မရှိ"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"ပြီးခဲ့သော\n၂၄ နာရီ"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"ပြီးခဲ့သည့်\n၇ ရက်"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ရာခိုင်နှုန်း"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို Android က ကာကွယ်ထားသည်။ သင့်ဒေတာကို ဤစက်ပစ္စည်းပေါ်တွင် စီမံသောကြောင့် ဤအက်ပ်၏ ခွင့်ပြုချက်အသုံးပြုမှုကို အခြေအနေပြဘား (သို့) သင့်ကိုယ်ရေးအချက်အလက် လုံခြုံမှု ဒက်ရှ်ဘုတ်ပေါ်တွင် မပြပါ။"</string>
<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>
@@ -520,7 +560,14 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"ကင်မရာသုံးခွင့် ပိတ်ထားသည်"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"မိုက်ခရိုဖုန်းသုံးခွင့် ပိတ်ထားသည်"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"တည်နေရာသုံးခွင့် ပိတ်ထားသည်"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"သတင်းနှင့်ဖျော်ဖြေရေး အက်ပ်များအတွက်"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"လိုအပ်သောအက်ပ်များအတွက်"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ဤအက်ပ်ကို လိုအပ်သည်"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"သင့်ကားထုတ်လုပ်သူက ဤအက်ပ်ကို လိုအပ်သည်"</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 +578,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 +623,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> တွင် ဝင်ကြည့်ထားသည်"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"မနေ့ <xliff:g id="TIME_DATE">%1$s</xliff:g> တွင် ဝင်ကြည့်ထားသည်"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g> တွင် ဝင်ကြည့်ထားသည်"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"သင့်တစ်ခါသုံး စကားဝှက်သည် 132435 ဖြစ်သည်"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ကန့်သတ်ဆက်တင်"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"သင့်လုံခြုံရေးအတွက် ဤဆက်တင်ကို လောလောဆယ် မရနိုင်ပါ။"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"အက်ပ်ကို <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> သုံးခွင့် ငြင်းပယ်ထားသည်"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"အက်ပ်သည် သင်၏ ပုဂ္ဂိုလ်ရေးနှင့် ငွေကြေးဆိုင်ရာ အချက်အလက်များကို အန္တရာယ်ဖြစ်စေနိုင်သော သတိထားရမည့် ခွင့်ပြုချက်သုံးရန် တောင်းဆိုထားသည်။<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>အက်ပ်သည် ဤကန့်သတ်ထားသော ခွင့်ပြုချက်မရှိပါက ကောင်းစွာမလုပ်ဆောင်ခြင်း ဖြစ်နိုင်သည်။ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;သုံးခွင့်ပြုနည်းကို လေ့လာရန်&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"အက်ပ်အား မူရင်း <xliff:g id="ROLE_NAME">%1$s</xliff:g> အဖြစ် လုပ်ဆောင်ခွင့် ငြင်းပယ်ထားသည်"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"အက်ပ်သည် သင်၏ ပုဂ္ဂိုလ်ရေးနှင့် ငွေကြေးဆိုင်ရာ အချက်အလက်များကို အန္တရာယ်ဖြစ်စေနိုင်သော သတိထားရမည့် ခွင့်ပြုချက်များသုံးရန် တောင်းဆိုထားသည်။<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>အက်ပ်သည် ဤကန့်သတ်ထားသော ခွင့်ပြုချက်များမရှိပါက ကောင်းစွာမလုပ်ဆောင်ခြင်း ဖြစ်နိုင်သည်။ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;သုံးခွင့်ပြုနည်းကို လေ့လာရန်&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"အက်ပ်ကို သုံးခွင့် ငြင်းပယ်ထားသည်"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ဤခွင့်ပြုချက်သုံးခြင်းသည် သင်၏ ပုဂ္ဂိုလ်ရေးနှင့် ငွေကြေးဆိုင်ရာ အချက်အလက်များကို အန္တရာယ်ဖြစ်စေနိုင်သည်။<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>အက်ပ်သည် ဤကန့်သတ်ထားသော ခွင့်ပြုချက်မရှိပါက ကောင်းစွာမလုပ်ဆောင်ခြင်း ဖြစ်နိုင်သည်။ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;သုံးခွင့်ပြုနည်းကို လေ့လာရန်&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ပိုမိုလေ့လာရန်"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ခွင့်ပြုချက်တောင်းဆိုမှု ပိတ်ထားသည်"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ဤအက်ပ်သည် နောက်ထပ်ခွင့်ပြုချက်များကို တောင်းဆိုနေသော်လည်း တိုက်ရိုက်လွှင့်စက်ရှင်တွင် ခွင့်ပြုချက်များ ပေး၍မရပါ။ သင့်ဖုန်းတွင် ဦးစွာ ခွင့်ပြုချက်ပေးပါ။"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"အရေးပေါ်ဖုန်းခေါ်ခြင်း (သို့) စာတိုပို့ခြင်းအတွက်"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"အရေးပေါ်ဝန်ဆောင်မှုများထံ တည်နေရာ ပို့ထားသည်"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"အရေးပေါ် နံပါတ်ထံ ဖုန်းခေါ် (သို့) စာတိုပို့နေစဉ် ဤအက်ပ်သည် သင့်စက်ပစ္စည်း၏ တည်နေရာကို သုံးထားသည်။ အက်ပ်တွင် တည်နေရာခွင့်ပြုချက် မရှိလည်း (သို့) စက်ပစ္စည်းတည်နေရာကို ပိတ်ထားလည်း ဤသို့ဖြစ်နိုင်သည်။ "<a href="https://support.google.com/android/answer/9319337">"ပိုမိုလေ့လာရန်"</a></string>
</resources>
diff --git a/PermissionController/res/values-nb-v34/strings.xml b/PermissionController/res/values-nb-v34/strings.xml
index a7bb456d5..d3fa18592 100644
--- a/PermissionController/res/values-nb-v34/strings.xml
+++ b/PermissionController/res/values-nb-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Administrer apptilgang til helsedata"</string>
<string name="location_settings" msgid="8863940440881290182">"Posisjonstilgang"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"For apper og tjenester. Hvis denne innstillingen er av, kan mikrofondata fremdeles deles når du ringer et nødnummer"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"For apper og tjenester"</string>
</resources>
diff --git a/PermissionController/res/values-nb-watch/strings.xml b/PermissionController/res/values-nb-watch/strings.xml
index 3fc35f2c9..3a4f05c10 100644
--- a/PermissionController/res/values-nb-watch/strings.xml
+++ b/PermissionController/res/values-nb-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Kan ikke endres"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ja"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Avbryt"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Hele tiden"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Når appen er i bruk"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Hele tiden"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Når appen er i bruk"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Hele tiden"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Når appen er i bruk"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Hele tiden"</string>
</resources>
diff --git a/PermissionController/res/values-nb/strings.xml b/PermissionController/res/values-nb/strings.xml
index 1e5f58453..dffb9c5a2 100644
--- a/PermissionController/res/values-nb/strings.xml
+++ b/PermissionController/res/values-nb/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"tillatelser"</string>
<string name="cancel" msgid="8943320028373963831">"Avbryt"</string>
<string name="back" msgid="6249950659061523680">"Tilbake"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Lukk"</string>
<string name="available" msgid="6007778121920339498">"Tilgjengelig"</string>
<string name="blocked" msgid="9195547604866033708">"Blokkert"</string>
<string name="on" msgid="280241003226755921">"På"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Alle tillatelser"</string>
<string name="other_permissions" msgid="2901186127193849594">"Andre appfunksjoner"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Forespørsel om tillatelse"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Handlinger for å installere og avinstallere støttes ikke på Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Velg hva du vil gi <xliff:g id="APP_NAME">%1$s</xliff:g> tilgang til"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"<xliff:g id="APP_NAME">%1$s</xliff:g> er oppdatert. Velg hva du vil gi denne appen tilgang til."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Avbryt"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Tillat alltid alle"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Spør hver gang"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ikke tillat"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Gi begrenset tilgang"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Nøyaktig posisjon"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Omtrentlig posisjon"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Bruk nøyaktig posisjon"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Når nøyaktig posisjon er av, har apper tilgang til den omtrentlige posisjonen din"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Tillatelse: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Tilgang for denne appen: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g>-tilgang for denne appen på <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Se alle tillatelsene <xliff:g id="APP">%1$s</xliff:g> har"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Se alle apper med denne tillatelsen"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Vis bruk av assistentmikrofonen"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Fjern tillatelser hvis appen ikke brukes"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Fjern tillatelser og frigjør plass"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Sett appaktivitet på pause hvis ubrukt"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Administrer appen hvis den ikke brukes"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Fjern tillatelser, slett midlertidige filer, og stopp varsler"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Fjern tillatelser, slett midlertidige filer, stopp varsler, og arkiver appen"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"For å beskytte dataene dine fjernes tillatelser for denne appen hvis appen ikke brukes på noen måneder."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Hvis appen ikke brukes på noen måneder, fjernes disse tillatelsene for å beskytte dataene dine: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"For å beskytte dataene dine har tillatelser blitt fjernet fra apper du ikke har brukt på noen måneder."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Kan administrere alle filer"</string>
<string name="ask_header" msgid="2633816846459944376">"Spør hver gang"</string>
<string name="denied_header" msgid="903209608358177654">"Ikke tillatt"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> på <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Se flere apper som kan bruke alle filer"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dager}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# time}other{# timer}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notatapp"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apper du kan bruke til å ta notater på enheten"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notater"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Standard lommebokapp"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Lommebokapp"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"I lommebokapper kan du lagre kreditt- og stamkundekort, bilnøkler og andre ting for å få hjelp med ulike transaksjonstyper."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Vil du bruke <xliff:g id="APP_NAME">%1$s</xliff:g> som standard lommebokapp?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Ingen tillatelser er nødvendige"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Gjeldende standard"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ikke spør igjen"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Angi som standard"</string>
@@ -426,8 +437,9 @@
<string name="default_apps_more" msgid="4078194675848858093">"Flere standardapper"</string>
<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_apps_for_private_profile" msgid="2022024112144880785">"Standard for privat område"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Vis aktivering av assistenten"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Vis ikon i statusfeltet når mikrofonen brukes til å aktivere taleassistent"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til bilder og medier på enheten din?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke bilder og medier på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til kontaktene dine?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lese kontaktene dine på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til denne enhetens posisjon?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til posisjonen til &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Appen får bare tilgang til posisjonen når du bruker appen"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til denne enhetens posisjon?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til posisjonen til &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Denne appen vil kanskje ha tilgang til posisjonen din hele tiden, selv når du ikke bruker appen. "<annotation id="link">"Gi tillatelse i innstillingene."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Vil du endre posisjonstilgang for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Vil du endre posisjonstilgangen for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Denne appen vil ha tilgang til posisjonen din hele tiden, selv når du ikke bruker appen. "<annotation id="link">"Gi tillatelse i innstillingene."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tillatelse til å finne, koble til og fastslå den relative posisjonen til enheter i nærheten?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; finne, koble til og fastslå den relative posisjonen til enheter i nærheten på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tillatelse til å finne, koble til og fastslå den relative posisjonen til enheter i nærheten? "<annotation id="link">"Tillat i innstillingene."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Vil du endre posisjontilgangen til <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> fra omtrentlig til nøyaktig?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Vil du endre tilgangen <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> har til posisjon på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; fra omtrentlig til nøyaktig?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til denne enhetens omtrentlige posisjon?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; se den omtrentlige posisjonen til &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Nøyaktig"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Omtrentlig"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til kalenderen din?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke kalenderen din på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sende og se tekstmeldinger?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sende og se SMS-meldinger på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til bilder, medier og filer på enheten din?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke bilder, medier og filer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke &lt;b&gt;bilder, videoer, musikk og lyd&lt;/b&gt; på denne enheten?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke &lt;b&gt;bilder, videoer, musikk, lyd og andre filer&lt;/b&gt; på denne enheten?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke musikk og lyd på denne enheten?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke musikk og lyd på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke bilder og videoer på denne enheten?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke bilder og videoer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke flere bilder og videoer på denne enheten?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke flere bilder og videoer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan bare ta opp lyd mens du bruker den."</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta opp lyd på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Denne appen vil kanskje ta opp lyd hele tiden, selv når du ikke bruker den. "<annotation id="link">"Gi tillatelse i innstillingene."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Vil du endre mikrofontilgang for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Vil du endre mikrofontilgangen for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Denne appen vil ta opp lyd hele tiden, selv når du ikke bruker den. "<annotation id="link">"Gi tillatelse i innstillingene."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til den fysiske aktiviteten din?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; se fysisk aktivitet på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta bilder og spille inn video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta bilder og ta opp video på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Appen kan bare ta bilder og spille inn videoer mens du bruker den"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta bilder og spille inn videoer?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ta bilder og ta opp video på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Denne appen vil kanskje ta bilder og spille inn videoer hele tiden, selv når du ikke bruker den. "<annotation id="link">"Gi tillatelse i innstillingene."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Vil du endre kameratilgang for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Vil du endre kameratilgangen for &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Denne appen vil ta bilder og spille inn videoer hele tiden, selv når du ikke bruker den. "<annotation id="link">"Gi tillatelse i innstillingene."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til samtaleloggene dine?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke samtaleloggene på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ringe og administrere telefonsamtaler?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ringe og administrere telefonanrop på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til sensordata om de vitale tegnene dine?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; se sensordata om de vitale tegnene dine på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Denne appen vil ha tilgang til sensordata om de vitale tegnene dine hele tiden, selv når du ikke bruker den. For å gjøre denne endringen, "<annotation id="link">"gå til innstillingene."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Vil du gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til sensordataene om de vitale tegnene dine?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; se sensordataene om de vitale tegnene dine på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"For å gi denne appen tilgang til data fra kroppssensorer til enhver tid, selv når du ikke bruker den, "<annotation id="link">"gå til innstillingene."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Vil du fortsette å gi &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tilgang til data fra kroppssensorer mens appen er i bruk?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Vil du fortsette å la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bruke data fra kroppssensorer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; mens appen er i bruk?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sende deg varsler?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Vil du la &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sende deg varsler på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontrollerte tillatelser"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> har posisjonstilgang"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organisasjonen din lar <xliff:g id="APP_NAME">%1$s</xliff:g> se hvor du er"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ingen"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"De siste\n24 timene"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"De siste\n7 dagene"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> prosent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> beskyttes av Android. Siden dataene dine behandles på denne enheten, vises ikke bruken av tillatelser for denne appen i statusfeltet eller personvernoversikten."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> beskyttes av Android. Siden dataene dine behandles på denne enheten, vises ikke bruken av tillatelser for denne appen i personvernoversikten."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Enhetskameraet er blokkert"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"For apper og tjenester"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofondata kan fremdeles deles når du ringer et nødnummer."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Endre"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kameratilgang er av"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofontilgang er av"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Posisjonstilgang er av"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"For infotainment-apper"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"For påkrevde apper"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Denne appen er påkrevd"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Denne appen kreves av bilprodusenten"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Sikkerhet og personvern"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skann enheten"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Lukk"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Oppdateringer av datadeling"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Noen apper har endret hvordan de kan dele posisjonsdataene dine"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Innstillinger"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Åpnet <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Åpnet i går <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Åpnet <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Engangspassordet ditt er 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begrenset innstilling"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Av sikkerhetshensyn er denne innstillingen utilgjengelig for øyeblikket."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appens tilgang til <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ble avvist"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen ba om tilgang til en sensitiv tillatelse som kan utsette den personlige og økonomiske informasjonen din for fare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Det kan hende at appen ikke fungerer skikkelig uten denne begrensede tillatelsen. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Finn ut hvordan du gir tilgang&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen fikk ikke tillatelse til å være standard <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Appen ba om tilgang til sensitive tillatelser som kan utsette den personlige og økonomiske informasjonen din for fare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Det kan hende at appen ikke fungerer skikkelig uten disse begrensede tillatelsene. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Finn ut hvordan du gir tilgang&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Appens tilgang ble avvist"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Hvis du gir tilgang til denne tillatelsen, kan den personlige og økonomiske informasjonen din bli utsatt for fare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Det kan hende at appen ikke fungerer skikkelig uten denne begrensede tillatelsen. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Finn ut hvordan du gir tilgang&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Finn ut mer"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Forespørselen om tillatelse er skjult"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Denne appen ber om flere tillatelser, men du kan ikke gi tillatelser i en strømmeøkt. Gi tillatelsen på telefonen først."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"For nødanrop eller -meldinger"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Posisjonen er sendt til nødtjenester"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Denne appen leste posisjonen til enheten din under et anrop eller ved en melding til et nødnummer. Dette kan skje selv om enhetens posisjon er slått av eller appen ikke har posisjonstillatelse. "<a href="https://support.google.com/android/answer/9319337">"Finn ut mer"</a></string>
</resources>
diff --git a/PermissionController/res/values-ne-v34/strings.xml b/PermissionController/res/values-ne-v34/strings.xml
index 421c5a8e0..89bb4279f 100644
--- a/PermissionController/res/values-ne-v34/strings.xml
+++ b/PermissionController/res/values-ne-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-ne-watch/strings.xml b/PermissionController/res/values-ne-watch/strings.xml
index d5f1fc2ae..60374094c 100644
--- a/PermissionController/res/values-ne-watch/strings.xml
+++ b/PermissionController/res/values-ne-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"बदल्न मिल्दैन"</string>
<string name="generic_yes" msgid="2489207724988649846">"हुन्छ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"रद्द गर्नु…"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"सधैँ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"एप प्रयोग गर्दा"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"सधैँ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"एप प्रयोग गर्दा"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"सधैँ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"एप प्रयोग गर्दा"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"सधैँ"</string>
</resources>
diff --git a/PermissionController/res/values-ne/strings.xml b/PermissionController/res/values-ne/strings.xml
index aad36443d..066683ebb 100644
--- a/PermissionController/res/values-ne/strings.xml
+++ b/PermissionController/res/values-ne/strings.xml
@@ -21,19 +21,22 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"अनुमतिहरू"</string>
<string name="cancel" msgid="8943320028373963831">"रद्द गर्नुहोस्"</string>
<string name="back" msgid="6249950659061523680">"पछाडि जानुहोस्"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"उपलब्ध"</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" 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 +47,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 +74,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 +87,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,15 +111,13 @@
<!-- 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="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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear मा स्थापना/स्थापना रद्द गर्ने कारबाहीहरू समर्थित छैनन्।"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई केमाथि पहुँच राख्न दिने हो छनौट गर्नुहोस्"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; अद्यावधिक गरिएको छ। यस एपलाई केमाथि पहुँच राख्न दिने हो छनौट गर्नुहोस्।"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"रद्द गर्नुहोस्"</string>
@@ -183,28 +185,32 @@
<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="app_permission_button_allow_limited_access" msgid="8824410215149764113">"सीमित एक्सेस दिनुहोस्"</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_header_with_device_name" msgid="7193042925656173271">"यो एपलाई <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को <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_label_v3" msgid="693340578642156657">"एप प्रयोग गरिएको छैन भने व्यवस्थापन गर्नुहोस्"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"अनुमतिहरू रद्द गर्नुहोस्, अस्थायी फाइलहरू मेटाउनुहोस् र एपसम्बन्धी सूचना नपठाउनुहोस्"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"अनुमति रद्द गर्नुहोस्, अस्थायी फाइलहरू मेटाउनुहोस्, सूचना नपठाउनुहोस् र एप अभिलेखमा राख्नुहोस्"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"तपाईंका डेटाको सुरक्षार्थ यो एप केही महिनासम्म प्रयोग नगरिएका खण्डमा यसलाई दिइएका अनुमति रद्द गरिने छन्।"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"तपाईंका डेटाको सुरक्षार्थ यो एप केही महिनासम्म प्रयोग नगरिएका खण्डमा निम्न अनुमति रद्द गरिने छन्: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"तपाईंका डेटाको सुरक्षार्थ तपाईंले केही महिनादेखि प्रयोग नगरेका एपलाई दिइएका अनुमति रद्द गरिएका छन्।"</string>
@@ -214,7 +220,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,8 +256,9 @@
<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="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> को <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"सबै फाइल हेर्ने र प्रयोग गर्ने अनुमति भएका थप एपहरू हेर्नुहोस्"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{१ दिन}other{# दिन}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# घण्टा}other{# घण्टा}}"</string>
@@ -349,7 +356,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>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"डिफल्ट Wallet एप"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet एप"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"तपाईंलाई विभिन्न तरिकाले कारोबार गर्न सघाउन Wallet एपमा तपाईंका क्रेडिट कार्ड तथा लोयल्टी कार्ड, कार कीलगायतका कुराहरू पनि भण्डारण गर्न सकिन्छ।"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> लाई आफ्नो डिफल्ट Wallet एपका रूपमा सेट गर्ने हो?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"कुनै पनि अनुमति चाहिन्न"</string>
<string name="request_role_current_default" msgid="738722892438247184">"हालको डिफल्ट एप"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"फेरि नसोध्नुहोस्"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"डिफल्ट सेट गर्नुहोस्"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"थप डिफल्ट एपहरू"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"लिंकहरू खोल्दा"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"कार्यका लागि डिफल्ट"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"निजी स्पेसका लागि डिफल्ट एपहरू"</string>
<string name="default_app_none" msgid="9084592086808194457">"कुनै पनि होइन"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(सिस्टम डिफल्ट)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"कुनै पनि एप छैन"</string>
@@ -443,60 +456,87 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; मा भएको तपाईंको स्वास्थ्यसम्बन्धी आधारभूत विवरण उपलब्ध गराउने सेन्सरसम्बन्धी डेटा एक्सेस गर्ने अनुमति दिने हो?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"यो एपलाई जुनसुकै बेला (तपाईंले एप नचलाएका बेलासमेत) बडी सेन्सरसम्बन्धी डेटा हेर्न र प्रयोग गर्न दिन "<annotation id="link">"सेटिङमा जानुहोस्।"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई यो एप प्रयोग गरिँदै गरेका बेला &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; लाई &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"कुनै पनि होइन"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"विगत\n२४ घन्टा"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"पछिल्ला\n७ दिन"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> प्रतिशत"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Android ले <xliff:g id="APP_NAME">%1$s</xliff:g> सुरक्षित राख्छ। तपाईंको जानकारी यो डिभाइसमा नै प्रोसेस गरिने भएकाले यो एपले कुन कुन अनुमति प्रयोग गर्‍यो भन्ने कुरा स्ट्याटस बार वा तपाईंको गोपनीयतासम्बन्धी ड्यासबोर्डमा देखाइँदैन।"</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Android ले <xliff:g id="APP_NAME">%1$s</xliff:g> सुरक्षित राख्छ। तपाईंको जानकारी यो डिभाइसमा नै प्रोसेस गरिने भएकाले यो एपले कुन कुन अनुमति प्रयोग गर्‍यो भन्ने कुरा तपाईंको गोपनीयतासम्बन्धी ड्यासबोर्डमा देखाइँदैन।"</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"डिभाइसको क्यामेरा ब्लक गरिएको छ"</string>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"क्यामेरा एक्सेस अफ छ"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"माइक्रोफोन एक्सेस अफ गरिएको छ"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"लोकेसन एक्सेस अफ गरिएको छ"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"इन्फोटेनमेन्ट एपहरूका लागि"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"आवश्यक एपहरूका लागि"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"यो एप अनिवार्य रूपमा इन्स्टल गर्नु पर्छ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"तपाईंको कारको उत्पादकले व्यवस्था गरेबमोजिम यो एप अनिवार्य रूपमा इन्स्टल गर्नु पर्छ"</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>
@@ -573,7 +621,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 +630,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,8 +663,30 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> मा एक्सेस गरिएको"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"हिजो <xliff:g id="TIME_DATE">%1$s</xliff:g> मा एक्सेस गरिएको"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> मा एक्सेस गरिएको"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"तपाईंको एक पटके पासवर्ड 132435 हो"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"प्रतिबन्ध लगाइएका सेटिङ"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"तपाईंको खाताको सुरक्षार्थ यो सेटिङ हाल उपलब्ध छैन।"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"एपले <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> प्रयोग गर्न मागेको अनुमति अस्वीकार गरिएको छ"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"यो एपले तपाईंको व्यक्तिगत तथा वित्तीय जानकारी जोखिममा पार्न सक्ने खालको संवेदनशील अनुमति मागेको छ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>तपाईले उक्त प्रतिबन्धित अनुमति नदिएका खण्डमा यो एपले राम्रोसँग काम नगर्न सक्छ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अनुमति दिने तरिका सिक्नुहोस्&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"यो एपले डिफल्ट <xliff:g id="ROLE_NAME">%1$s</xliff:g> का रूपमा काम गर्न मागेको अनुमति अस्वीकार गरिएको छ"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"यो एपले तपाईंका व्यक्तिगत तथा वित्तीय जानकारी जोखिममा पार्न सक्ने खालका संवेदनशील अनुमतिहरू मागेको छ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>तपाईंले ती प्रतिबन्धित अनुमति नदिएका खण्डमा यो एपले राम्रोसँग काम नगर्न सक्छ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अनुमति दिने तरिका सिक्नुहोस्&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"एपले मागेको अनुमति अस्वीकार गरिएको छ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"तपाईंले यो अनुमति दिनुभयो भने तपाईंको व्यक्तिगत तथा वित्तीय जानकारी जोखिममा पर्न सक्छ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>तपाईंले यो प्रतिबन्धित अनुमति नदिएका खण्डमा यो एपले राम्रोसँग काम नगर्न सक्छ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;अनुमति दिने तरिका सिक्नुहोस्&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"थप जान्नुहोस्"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ठिक छ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"अनुमतिसम्बन्धी अनुरोध रद्द गरिएको छ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"यो एपले अतिरिक्त अनुमति मागिरहेको छ तर स्ट्रिमिङ सत्रमा अनुमति दिन मिल्दैन। सर्वप्रथम आफ्नो फोनमा अनुमति दिनुहोस्।"</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
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-v34/strings.xml b/PermissionController/res/values-nl-v34/strings.xml
index b3265d254..8e0464cbb 100644
--- a/PermissionController/res/values-nl-v34/strings.xml
+++ b/PermissionController/res/values-nl-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Beheer de toegang van apps tot je gezondheidsgegevens"</string>
<string name="location_settings" msgid="8863940440881290182">"Locatietoegang"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"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="6846532794702613851">"Voor apps en services"</string>
</resources>
diff --git a/PermissionController/res/values-nl-watch/strings.xml b/PermissionController/res/values-nl-watch/strings.xml
index 454676644..4b70e1174 100644
--- a/PermissionController/res/values-nl-watch/strings.xml
+++ b/PermissionController/res/values-nl-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Niet aanpasbaar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ja"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Annuleren"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Altijd"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Bij gebruik van de app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Altijd"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Bij gebruik van de app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Altijd"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Bij gebruik van de app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Altijd"</string>
</resources>
diff --git a/PermissionController/res/values-nl/strings.xml b/PermissionController/res/values-nl/strings.xml
index f845b1de4..bf0af920f 100644
--- a/PermissionController/res/values-nl/strings.xml
+++ b/PermissionController/res/values-nl/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"rechten"</string>
<string name="cancel" msgid="8943320028373963831">"Annuleren"</string>
<string name="back" msgid="6249950659061523680">"Terug"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Sluiten"</string>
<string name="available" msgid="6007778121920339498">"Beschikbaar"</string>
<string name="blocked" msgid="9195547604866033708">"Geblokkeerd"</string>
<string name="on" msgid="280241003226755921">"Aan"</string>
@@ -34,6 +35,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 +52,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Alle rechten"</string>
<string name="other_permissions" msgid="2901186127193849594">"Andere app-mogelijkheden"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Rechtenverzoek"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Acties voor installeren/verwijderen niet ondersteund op Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Kiezen waartoe &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang krijgt"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is geüpdatet. Kies waartoe je deze app toegang wilt geven."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Annuleren"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Altijd alles toestaan"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Altijd vragen"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Niet toestaan"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Beperkte toegang toestaan"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Exacte locatie"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Geschatte locatie"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Exacte locatie gebruiken"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Als de exacte locatie uitstaat, hebben apps toegang tot je geschatte locatie"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Rechten: <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Toegang tot <xliff:g id="PERM">%1$s</xliff:g> voor deze app"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Toegang tot <xliff:g id="PERM">%1$s</xliff:g> voor deze app op <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Alle rechten van <xliff:g id="APP">%1$s</xliff:g> bekijken"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Alle apps met dit recht bekijken"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Gebruik van Assistent-microfoon tonen"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Rechten intrekken als app niet wordt gebruikt"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Rechten intrekken en ruimte vrijmaken"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"App-activiteit onderbreken indien niet gebruikt"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"App beheren indien ongebruikt"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Verwijder rechten en tijdelijke bestanden, en stop meldingen"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Verwijder rechten en tijdelijke bestanden, stop meldingen en archiveer de app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Om je gegevens te beschermen worden de rechten voor deze app verwijderd als de app een aantal maanden niet is gebruikt."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Om je gegevens te beschermen worden de volgende rechten ingetrokken als de app een paar maanden niet is gebruikt: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Om je gegevens te beschermen zijn de rechten verwijderd van apps die al een paar maanden niet zijn gebruikt."</string>
@@ -219,7 +224,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Alle bestanden beheren"</string>
<string name="ask_header" msgid="2633816846459944376">"Altijd vragen"</string>
<string name="denied_header" msgid="903209608358177654">"Niet toegestaan"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> in <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Bekijk meer apps die toegang tot alle bestanden hebben."</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dagen}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# uur}other{# uur}}"</string>
@@ -349,27 +355,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notitie-app"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps waarmee je notities op je apparaat kunt maken"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notities"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Standaard portemonnee-app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Portemonnee-app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"In portemonnee-apps kun je je creditcards en klantenkaarten, autosleutels en andere zaken opslaan om je te helpen bij verschillende vormen van transacties."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> instellen als je standaard portemonnee-app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Geen rechten nodig"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Huidige standaard-app"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Niet meer vragen"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Standaard instellen"</string>
@@ -426,8 +437,9 @@
<string name="default_apps_more" msgid="4078194675848858093">"Meer standaard-apps"</string>
<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_apps_for_private_profile" msgid="2022024112144880785">"Standaard voor privégedeelte"</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,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s en media op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je contacten op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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; toegang geven tot de locatie van dit apparaat?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de locatie van &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de locatie van &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Toegang tot locatie wijzigen voor &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Toestaan dat &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; apparaten in de buurt vindt, ermee verbinding maakt en de relatieve positie ervan bepaalt op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"Locatietoegang van <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; wijzigen van geschat in exact?"</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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de geschatte locatie van &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je agenda op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om sms-berichten te sturen en te bekijken op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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; toegang geven tot foto\'s, media en bestanden op je apparaat?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s, media en bestanden op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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; 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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot muziek en audio op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot foto\'s en video\'s op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_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_device_aware_more_photos" msgid="1703469013613723053">"&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 &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om audio op te nemen op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Toegang tot microfoon wijzigen voor &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot je fysieke activiteit op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om foto\'s te maken en video op te nemen op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om foto\'s te maken en video op te nemen op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Toegang tot camera wijzigen voor &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot de gesprekslijsten van je telefoon op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om telefoongesprekken te voeren en te beheren op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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; 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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot sensorgegevens over je vitale functies op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang geven tot sensorgegevens over je vitale functies op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toegang blijven geven tot gegevens van de lichaamssensor op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; terwijl de app in gebruik is?"</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>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; toestaan om je meldingen te sturen op &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Beheerde rechten"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> heeft toegang tot je locatie"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Je organisatie geeft <xliff:g id="APP_NAME">%1$s</xliff:g> toegang tot je locatie"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Geen"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Afgelopen\n24 uur"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Afgelopen\n7 dagen"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> procent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> wordt beveiligd door Android. Omdat je gegevens worden verwerkt op dit apparaat, wordt het gebruik van rechten door deze app niet getoond in de statusbalk of op je privacydashboard."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> wordt beveiligd door Android. Omdat je gegevens worden verwerkt op dit apparaat, wordt het gebruik van rechten door deze app niet getoond op je privacydashboard."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Camera van apparaat is geblokkeerd"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Voor apps en services"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Microfoongegevens kunnen nog altijd worden gedeeld als je een alarmnummer belt."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Wijzigen"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Cameratoegang staat uit"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Microfoontoegang staat uit"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Locatietoegang staat uit"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Voor infotainment-apps"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Voor vereiste apps"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Deze app is vereist"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Deze app wordt vereist door de fabrikant van je auto"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Beveiliging en privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Apparaat scannen"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Sluiten"</string>
@@ -581,7 +628,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 +655,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Updates voor gegevens delen"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Sommige apps kunnen je locatiegegevens nu op een andere manier delen"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Instellingen"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Geopend: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Geopend: gisteren om <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Geopend: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> om <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Je eenmalige wachtwoord is 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Beperkte instelling"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ter beveiliging is deze instelling op dit moment niet beschikbaar."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"App heeft geen toegang gekregen tot <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"De app heeft toegang gevraagd tot een gevoelig recht, waardoor je persoonlijke en financiële informatie risico kunnen lopen.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Het is mogelijk dat de app niet goed werkt zonder dit beperkte recht. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Meer informatie over hoe je toegang geeft&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"App heeft geen toegang gekregen om de standaard <xliff:g id="ROLE_NAME">%1$s</xliff:g> te worden"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"De app heeft toegang gevraagd tot gevoelige rechten, waardoor je persoonlijke en financiële informatie risico kunnen lopen.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Het is mogelijk dat de app niet goed werkt zonder deze beperkte rechten. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Meer informatie over hoe je toegang geeft&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"App heeft geen toegang gekregen"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Als je toegang geeft tot dit recht, kunnen je persoonlijke en financiële informatie risico lopen.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Het is mogelijk dat de app niet goed werkt zonder dit beperkte recht. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Meer informatie over hoe je toegang geeft&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Meer informatie"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Verzoek om rechten onderdrukt"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Deze app vraagt om extra rechten, maar je kunt geen rechten verlenen tijdens een streamingsessie. Verleen het recht eerst op je telefoon."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Voor noodoproep of -tekstbericht"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Locatie naar de hulpdiensten gestuurd"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Deze app heeft toegang gehad tot de locatie van je apparaat tijdens een gesprek met of tekstbericht naar een alarmnummer. Dit kan ook gebeuren als de app geen locatierechten heeft of als de apparaatlocatie uitstaat. "<a href="https://support.google.com/android/answer/9319337">"Meer informatie"</a></string>
</resources>
diff --git a/PermissionController/res/values-or-v34/strings.xml b/PermissionController/res/values-or-v34/strings.xml
index 2994a3f25..8e9b52f97 100644
--- a/PermissionController/res/values-or-v34/strings.xml
+++ b/PermissionController/res/values-or-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-or-watch/strings.xml b/PermissionController/res/values-or-watch/strings.xml
index ef9515f25..579128b3f 100644
--- a/PermissionController/res/values-or-watch/strings.xml
+++ b/PermissionController/res/values-or-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ଏହା ବଦଳାଯାଇପାରିବ ନାହିଁ"</string>
<string name="generic_yes" msgid="2489207724988649846">"ହଁ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ବାତିଲ କରନ୍ତୁ"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ସର୍ବଦା"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ଆପ ବ୍ୟବହାର କରିବା ସମୟରେ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ସର୍ବଦା"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ଆପ ବ୍ୟବହାର କରିବା ସମୟରେ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ସର୍ବଦା"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ଆପ ବ୍ୟବହାର କରିବା ସମୟରେ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ସର୍ବଦା"</string>
</resources>
diff --git a/PermissionController/res/values-or/strings.xml b/PermissionController/res/values-or/strings.xml
index 840eae10f..ff0e68809 100644
--- a/PermissionController/res/values-or/strings.xml
+++ b/PermissionController/res/values-or/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ଅନୁମତିଗୁଡ଼ିକ"</string>
<string name="cancel" msgid="8943320028373963831">"ବାତିଲ କରନ୍ତୁ"</string>
<string name="back" msgid="6249950659061523680">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"ଉପଲବ୍ଧ ଅଛି"</string>
<string name="blocked" msgid="9195547604866033708">"ବ୍ଲକ କରାଯାଇଛି"</string>
<string name="on" msgid="280241003226755921">"ଚାଲୁ ଅଛି"</string>
@@ -34,6 +36,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 +63,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 +74,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>
@@ -114,8 +118,6 @@
<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 ୱିୟର୍‌"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"ୱିୟର୍‌ରେ ଇନଷ୍ଟଲ୍‍/ଅନଇନଷ୍ଟଲ୍‍ କାର୍ଯ୍ୟଗୁଡ଼ିକ ସମର୍ଥନ କରେନାହିଁ।"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ଆକ୍ସେସ୍‍ କରିବା ପାଇଁ କେଉଁସବୁ ଅନୁମତି ଦିଆଯିବ, ତାହା ବାଛନ୍ତୁ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ଅପଡେଟ୍‍ କରାଯାଇଛି। ଏହି ଆପ୍‍ କେଉଁସବୁ ଆକ୍ସେସ୍‍ କରିପାରିବ, ତାହା ବାଛନ୍ତୁ।"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ବାତିଲ କରନ୍ତୁ"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"ସୀମିତ ଆକ୍ସେସକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>ରେ ଏହି ଆପ ପାଇଁ <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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"ଆପକୁ ବ୍ୟବହାର କରାଯାଉନଥିଲେ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ଅନୁମତିଗୁଡ଼ିକୁ କାଢ଼ି ଦିଅନ୍ତୁ, ଅସ୍ଥାୟୀ ଫାଇଲଗୁଡ଼ିକୁ ଡିଲିଟ କରନ୍ତୁ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ଅନୁମତିଗୁଡ଼ିକୁ କାଢ଼ି ଦିଅନ୍ତୁ, ଅସ୍ଥାୟୀ ଫାଇଲଗୁଡ଼ିକୁ ଡିଲିଟ କରନ୍ତୁ, ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବନ୍ଦ କରନ୍ତୁ ଏବଂ ଆପକୁ ଆର୍କାଇଭ କରନ୍ତୁ"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ଯଦି ଏହି ଆପକୁ କିଛି ମାସ ପାଇଁ ବ୍ୟବହାର କରାଯାଇନାହିଁ, ତେବେ ଆପଣଙ୍କ ଡାଟାର ସୁରକ୍ଷା ନିମନ୍ତେ ଏହାର ଅନୁମତିଗୁଡ଼ିକୁ କାଢ଼ି ଦିଆଯିବ।"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"ଯଦି ଏହି ଆପକୁ କିଛି ମାସ ପାଇଁ ବ୍ୟବହାର କରାଯାଇନାହିଁ ତେବେ ଆପଣଙ୍କ ଡାଟାର ସୁରକ୍ଷା ନିମନ୍ତେ, ନିମ୍ନୋକ୍ତ ଅନୁମତିଗୁଡ଼ିକୁ କାଢ଼ି ଦିଆଯିବ: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ଆପଣଙ୍କ ଡାଟାର ସୁରକ୍ଷା ପାଇଁ, ଆପଣ କିଛି ମାସ ହେଲା ବ୍ୟବହାର କରିନଥିବା ଆପଗୁଡ଼ିକରୁ ଅନୁମତିଗୁଡ଼ିକୁ କାଢ଼ି ଦିଆଯାଇଛି।"</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ସମସ୍ତ ଫାଇଲ୍ ପରିଚାଳନା କରିବାକୁ ଅନୁମତି ଦିଆଯାଇଛି"</string>
<string name="ask_header" msgid="2633816846459944376">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
<string name="denied_header" msgid="903209608358177654">"ଅନୁମତି ନାହିଁ"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>ର <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ସମସ୍ତ ଫାଇଲକୁ ଆକ୍ସେସ କରିପାରୁଥିବା ଅଧିକ ଆପ୍ସ ଦେଖନ୍ତୁ"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ଦିନ}other{# ଦିନ}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ଘଣ୍ଟା}other{# ଘଣ୍ଟା}}"</string>
@@ -352,17 +359,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 +391,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>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ଡିଫଲ୍ଟ ୱାଲେଟ ଆପ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"ୱାଲେଟ ଆପ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"ବିଭିନ୍ନ ପ୍ରକାରର ଟ୍ରାଞ୍ଜେକସନରେ ସାହାଯ୍ୟ କରିବାକୁ ୱାଲେଟ ଆପ୍ସ ଆପଣଙ୍କ କ୍ରେଡିଟ ଓ ଲୟାଲ୍ଟି କାର୍ଡ, କାର କୀ ଏବଂ ଅନ୍ୟ ଜିନିଷଗୁଡ଼ିକୁ ଷ୍ଟୋର କରିପାରିବ।"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ ୱାଲେଟ ଆପ ଭାବେ ସେଟ କରିବେ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"କୌଣସି ଅନୁମତି ଆବଶ୍ୟକ ନାହିଁ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ସମ୍ପ୍ରତ୍ତି ଡିଫଲ୍ଟ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ଆଉ ପଚାରନ୍ତୁ ନାହିଁ"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ଡିଫଲ୍ଟ ଭାବେ ସେଟ୍ କରନ୍ତୁ"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"ଅଧିକ ଡିଫଲ୍ଟଗୁଡ଼ିକ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ଓପନିଂ ଲିଙ୍କ୍"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"କାର୍ଯ୍ୟ ପାଇଁ ଡିଫଲ୍ଟ ଅଛି"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ପ୍ରାଇଭେଟ ସ୍ପେସ ପାଇଁ ଡିଫଲ୍ଟ"</string>
<string name="default_app_none" msgid="9084592086808194457">"କିଛି ଆପ ସେଟ କରାଯାଇନାହିଁ"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ସିଷ୍ଟମ୍ ଡିଫଲ୍ଟ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"କୌଣସି ଆପ୍‌ ନାହିଁ"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଥିବା ଫଟୋ ଏବଂ ମିଡିଆକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଆପଣଙ୍କ କଣ୍ଟାକ୍ଟଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଥିବା ଆପଣଙ୍କ କଣ୍ଟାକ୍ଟଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"ଏହି ଡିଭାଇସର ଲୋକେସନ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ର ଲୋକେସନକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ର ଲୋକେସନକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ପାଇଁ ଲୋକେସନ ଆକ୍ସେସକୁ ପରିବର୍ତ୍ତନ କରିବେ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"ଆପଣ ଆପ୍ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ, ଏହି ଆପ୍ ସବୁ ସମୟରେ ଆପଣଙ୍କର ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରିବାକୁ ଚାହେଁ। "<annotation id="link">"ସେଟିଂସରେ ଅନୁମତି ଦିଅନ୍ତୁ।"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକର ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ଖୋଜିବା, କନେକ୍ଟ ଏବଂ ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକର ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ଖୋଜିବା, କନେକ୍ଟ ଓ ସ୍ଥିର କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକର ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ଖୋଜିବା, ସଂଯୋଗ ଏବଂ ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ? "<annotation id="link">"ସେଟିଂସରେ ଅନୁମତି ଦିଅନ୍ତୁ।"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>ର ଲୋକେସନ୍ ଆକ୍ସେସକୁ ଆନୁମାନିକରୁ ସଠିକକୁ ବଦଳାଇବେ?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ର ଆନୁମାନିକ ଲୋକେସନକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆପଣଙ୍କ କେଲେଣ୍ଡରକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ SMS ମେସେଜ୍‍ ପଠାଇବା ଓ ଦେଖିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ 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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଥିବା ଫଟୋ, ମିଡିଆ ଏବଂ ଫାଇଲକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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;ଫଟୋ, ଭିଡିଓ, ମ୍ୟୁଜିକ, ଅଡିଓ ଓ ଅନ୍ୟ ଫାଇଲ&lt;/b&gt; ଆକ୍ସେସ କରିବା ପାଇଁ &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଥିବା ମ୍ୟୁଜିକ ଏବଂ ଅଡିଓକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଥିବା ଫଟୋ ଏବଂ ଭିଡିଓକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"ଏହି ଡିଭାଇସରେ ଥିବା ଅଧିକ ଫଟୋ ଏବଂ ଭିଡିଓକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଥିବା ଅଧିକ ଫଟୋ ଏବଂ ଭିଡିଓକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଅଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଅଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ପାଇଁ ମାଇକ୍ରୋଫୋନ ଆକ୍ସେସକୁ ପରିବର୍ତ୍ତନ କରିବେ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆପଣଙ୍କ ଶାରୀରିକ କାର୍ଯ୍ୟକଳାପକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଫଟୋ ଉଠାଇବାକୁ ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିବାକୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଫଟୋ ଉଠାଇବା ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଫଟୋ ଉଠାଇବା ଏବଂ ଭିଡିଓ ରେକର୍ଡ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ପାଇଁ କେମେରା ଆକ୍ସେସକୁ ପରିବର୍ତ୍ତନ କରିବେ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆପଣଙ୍କ ଫୋନ କଲ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଫୋନ କଲଗୁଡ଼ିକ କରିବା ଏବଂ ସେଗୁଡ଼ିକୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଆପଣଙ୍କ ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ଲକ୍ଷଣଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର ଡାଟା ଆକ୍ସେସ କରିବା ପାଇଁ ଅନୁମତି ଦେବେ କି?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆପଣଙ୍କ ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ସଙ୍କେତ ବିଷୟରେ ସେନ୍ସର ଡାଟାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆପଣଙ୍କ ଗୁରୁତ୍ୱପୂର୍ଣ୍ଣ ସଙ୍କେତ ବିଷୟରେ ସେନ୍ସର ଡାଟାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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_device_aware_sensors" msgid="5542771499929819675">"ଆପକୁ ବ୍ୟବହାର କରାଯାଉଥିବା ବେଳେ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ବଡି ସେନ୍ସର ଡାଟାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବା ଜାରି ରଖିବେ?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ରେ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତି ପଠାଇବା ପାଇଁ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"କିଛି ନାହିଁ"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"ଗତ\n24 ଘଣ୍ଟା"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"ଗତ\n7 ଦିନ"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ଶତକଡ଼ା"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ Android ଦ୍ୱାରା ସୁରକ୍ଷିତ କରାଯାଇଛି। ଏହି ଡିଭାଇସରେ ଆପଣଙ୍କ ଡାଟା ପ୍ରକ୍ରିୟାକରଣ କରାଯାଇଥିବା ଯୋଗୁଁ ଏହି ଆପର ଅନୁମତି ବ୍ୟବହାରର ସୂଚନା ଷ୍ଟାଟସ୍ ବାରରେ କିମ୍ବା ଆପଣଙ୍କ ଗୋପନୀୟତା ଡ୍ୟାସବୋର୍ଡରେ ଦେଖାଯାଉ ନାହିଁ।"</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"କେମେରା ଆକ୍ସେସ ବନ୍ଦ ଅଛି"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"ମାଇକ୍ରୋଫୋନ ଆକ୍ସେସ ବନ୍ଦ ଅଛି"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"ଲୋକେସନ ଆକ୍ସେସ ବନ୍ଦ ଅଛି"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ଇନଫୋଟେନମେଣ୍ଟ ଆପ୍ସ ପାଇଁ"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ଆବଶ୍ୟକୀୟ ଆପ୍ସ ପାଇଁ"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ଏହି ଆପ ଆବଶ୍ୟକ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ଆପଣଙ୍କ କାରର ନିର୍ମାତାଙ୍କ ଦ୍ୱାରା ଏହି ଆପ ଆବଶ୍ୟକ"</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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇଛି"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ଗତକାଲି <xliff:g id="TIME_DATE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇଛି"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇଛି"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ଆପଣଙ୍କ ଗୋଟିଏ ଥରର ପାସୱାର୍ଡ ହେଉଛି 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ ଏହି ସେଟିଂ ବର୍ତ୍ତମାନ ଅନୁପଲବ୍ଧ ଅଟେ।"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>କୁ ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଏବଂ ଆର୍ଥିକ ସୂଚନାକୁ ବିପଦରେ ପକାଇପାରୁଥିବା ଏକ ସମ୍ବେଦନଶୀଳ ଅନୁମତିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପଟି ଅନୁରୋଧ କରିଛି।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ଏହା ସମ୍ଭବ ଯେ ଏହି ପ୍ରତିବନ୍ଧିତ ଅନୁମତି ବିନା ଆପ ସଠିକ ଭାବେ କାର୍ଯ୍ୟ କରିବ ନାହିଁ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ଆକ୍ସେସକୁ କିପରି ଅନୁମତି ଦେବେ ତାହା ଜାଣନ୍ତୁ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ଡିଫଲ୍ଟ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ହେବା ପାଇଁ ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଏବଂ ଆର୍ଥିକ ସୂଚନାକୁ ବିପଦରେ ପକାଇପାରୁଥିବା ସମ୍ବେଦନଶୀଳ ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପଟି ଅନୁରୋଧ କରିଛି।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ଏହା ସମ୍ଭବ ଯେ ଏହି ପ୍ରତିବନ୍ଧିତ ଅନୁମତିଗୁଡ଼ିକ ବିନା ଆପ ସଠିକ ଭାବେ କାର୍ଯ୍ୟ କରିବ ନାହିଁ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ଆକ୍ସେସକୁ କିପରି ଅନୁମତି ଦେବେ ତାହା ଜାଣନ୍ତୁ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ଆପର ଆକ୍ସେସକୁ ଅଗ୍ରାହ୍ୟ କରାଯାଇଛି"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ଏବଂ ଆର୍ଥିକ ସୂଚନାକୁ ବିପଦରେ ପକାଇପାରେ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ଏହା ସମ୍ଭବ ଯେ ଏହି ପ୍ରତିବନ୍ଧିତ ଅନୁମତି ବିନା ଆପ ସଠିକ ଭାବେ କାର୍ଯ୍ୟ କରିବ ନାହିଁ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ଆକ୍ସେସକୁ କିପରି ଅନୁମତି ଦେବେ ତାହା ଜାଣନ୍ତୁ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ଠିକ ଅଛି"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ଅନୁମତି ଅନୁରୋଧକୁ ବନ୍ଦ କରାଯାଇଛି"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ଏହି ଆପ ଅତିରିକ୍ତ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି, ଏକ ଷ୍ଟ୍ରିମିଂ ସେସନରେ ଅନୁମତି ଦିଆଯାଇପାରିବ ନାହିଁ। ପ୍ରଥମେ ଆପଣଙ୍କ ଫୋନରେ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-pa-v34/strings.xml b/PermissionController/res/values-pa-v34/strings.xml
index c0b70fb57..0f389c80d 100644
--- a/PermissionController/res/values-pa-v34/strings.xml
+++ b/PermissionController/res/values-pa-v34/strings.xml
@@ -17,11 +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="location_settings" msgid="8863940440881290182">"ਟਿਕਾਣਾ ਪਹੁੰਚ"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ। ਇਸ ਸੈਟਿੰਗ ਦੇ ਬੰਦ ਹੋਣ \'ਤੇ, ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਐਮਰਜੈਂਸੀ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰਦੇ ਹੋ ਤਾਂ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਡਾਟੇ ਨੂੰ ਫਿਰ ਵੀ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"ਐਪਾਂ ਅਤੇ ਸੇਵਾਵਾਂ ਲਈ"</string>
</resources>
diff --git a/PermissionController/res/values-pa-watch/strings.xml b/PermissionController/res/values-pa-watch/strings.xml
index 8a46aef6f..3bac90bee 100644
--- a/PermissionController/res/values-pa-watch/strings.xml
+++ b/PermissionController/res/values-pa-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"</string>
<string name="generic_yes" msgid="2489207724988649846">"ਹਾਂ"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ਰੱਦ ਕਰੋ"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ਹਰ ਸਮੇਂ"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ਐਪ ਦੀ ਵਰਤੋਂ ਦੌਰਾਨ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ਹਰ ਸਮੇਂ"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ਐਪ ਦੀ ਵਰਤੋਂ ਦੌਰਾਨ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ਹਰ ਸਮੇਂ"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ਐਪ ਦੀ ਵਰਤੋਂ ਦੌਰਾਨ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ਹਰ ਸਮੇਂ"</string>
</resources>
diff --git a/PermissionController/res/values-pa/strings.xml b/PermissionController/res/values-pa/strings.xml
index 035bd2298..c860460e3 100644
--- a/PermissionController/res/values-pa/strings.xml
+++ b/PermissionController/res/values-pa/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ਇਜਾਜ਼ਤਾਂ"</string>
<string name="cancel" msgid="8943320028373963831">"ਰੱਦ ਕਰੋ"</string>
<string name="back" msgid="6249950659061523680">"ਪਿੱਛੇ"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"ਉਪਲਬਧ ਹੈ"</string>
<string name="blocked" msgid="9195547604866033708">"ਬਲਾਕ ਹੈ"</string>
<string name="on" msgid="280241003226755921">"ਚਾਲੂ ਹੈ"</string>
@@ -34,6 +36,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 +51,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 +63,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 +74,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>
@@ -114,8 +118,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear \'ਤੇ ਸਥਾਪਤ/ਅਣਸਥਾਪਤ ਕਰਨ ਦੀਆਂ ਕਾਰਵਾਈਆਂ ਸਮਰਥਿਤ ਨਹੀਂ ਹਨ।"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"ਇਹ ਚੁਣੋ ਕਿ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਕਿਸ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ। ਇਹ ਚੁਣੋ ਕਿ ਇਸ ਐਪ ਨੂੰ ਕਿਸ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣੀ ਹੈ।"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ਰੱਦ ਕਰੋ"</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>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"ਸੀਮਤ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦਿਓ"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> \'ਤੇ ਇਸ ਐਪ ਲਈ <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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"ਵਰਤੋਂ ਵਿੱਚ ਨਾ ਹੋਣ \'ਤੇ, ਐਪ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"ਇਜਾਜ਼ਤਾਂ ਹਟਾਓ, ਅਸਥਾਈ ਫ਼ਾਈਲਾਂ ਮਿਟਾਓ ਅਤੇ ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"ਇਜਾਜ਼ਤਾਂ ਹਟਾਓ, ਅਸਥਾਈ ਫ਼ਾਈਲਾਂ ਮਿਟਾਓ, ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ ਅਤੇ ਐਪ ਨੂੰ ਪੁਰਾਲੇਖਬੱਧ ਕਰੋ"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ਤੁਹਾਡੀ ਡਾਟਾ ਸੁਰੱਖਿਆ ਲਈ, ਜੇ ਇਹ ਐਪ ਕੁਝ ਮਹੀਨਿਆਂ ਲਈ ਵਰਤੀ ਨਹੀਂ ਗਈ, ਤਾਂ ਇਸ ਐਪ ਲਈ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"ਤੁਹਾਡੀ ਡਾਟਾ ਸੁਰੱਖਿਆ ਲਈ, ਜੇ ਇਹ ਐਪ ਕੁਝ ਮਹੀਨਿਆਂ ਲਈ ਵਰਤੀ ਨਹੀਂ ਗਈ, ਤਾਂ ਅੱਗੇ ਦਿੱਤੀਆਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ਤੁਹਾਡੀ ਡਾਟਾ ਸੁਰੱਖਿਆ ਲਈ, ਜੋ ਐਪਾਂ ਤੁਸੀਂ ਕੁਝ ਮਹੀਨਿਆਂ ਤੋਂ ਨਹੀਂ ਵਰਤੀਆਂ ਉਹਨਾਂ ਐਪਾਂ ਤੋਂ ਇਜਾਜ਼ਤਾਂ ਨੂੰ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ।"</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ਸਾਰੀਆਂ ਫ਼ਾਈਲਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦਿੱਤਾ ਗਿਆ"</string>
<string name="ask_header" msgid="2633816846459944376">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
<string name="denied_header" msgid="903209608358177654">"ਗੈਰ-ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> \'ਤੇ <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ਸਾਰੀਆਂ ਫ਼ਾਈਲਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਣ ਵਾਲੀਆਂ ਹੋਰ ਐਪਾਂ ਦੇਖੋ"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ਦਿਨ}one{# ਦਿਨ}other{# ਦਿਨ}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ਘੰਟਾ}one{# ਘੰਟਾ}other{# ਘੰਟੇ}}"</string>
@@ -349,7 +356,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>
@@ -398,9 +405,14 @@
<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="role_wallet_label" msgid="3719419175656204207">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਾਲੇਟ ਐਪ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet ਐਪ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"ਲੈਣ-ਦੇਣ ਦੀਆਂ ਵੱਖ-ਵੱਖ ਕਿਸਮਾਂ ਸੰਬੰਧੀ ਮਦਦ ਕਰਨ ਲਈ, ਵਾਲੇਟ ਐਪਾਂ ਤੁਹਾਡੇ ਕ੍ਰੈਡਿਟ ਅਤੇ ਵਫ਼ਾਦਾਰੀ ਕਾਰਡਾਂ, ਕਾਰ ਦੀਆਂ ਕੁੰਜੀਆਂ ਅਤੇ ਹੋਰ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਟੋਰ ਕਰ ਸਕਦੀਆਂ ਹਨ।"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਾਲੇਟ ਐਪ ਵਜੋਂ ਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ਕਿਸੇ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ਮੌਜੂਦਾ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"ਹੋਰ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"ਖੁੱਲ੍ਹਣ ਵਾਲੇ ਲਿੰਕ"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ਕੰਮ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="default_app_none" msgid="9084592086808194457">"ਕੋਈ ਨਹੀਂ"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ਸਿਸਟਮ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ਕੋਈ ਐਪਾਂ ਨਹੀਂ"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"ਕੀ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; \'ਤੇ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਲਈ ਟਿਕਾਣਾ ਪਹੁੰਚ ਨੂੰ ਬਦਲਣਾ ਹੈ?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"ਇਸ ਐਪ ਨੂੰ ਹਰ ਵੇਲੇ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਚਾਹੀਦੀ ਹੈ, ਭਾਵੇਂ ਤੁਸੀਂ ਐਪ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੇ ਹੋ ਜਾਂ ਨਾ। "<annotation id="link">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਆਗਿਆ ਦਿਓ।"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਲੱਭਣ, ਉਨ੍ਹਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਅਤੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੇਣਾ ਹੈ?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"ਕੀ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; \'ਤੇ <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_device_aware_coarselocation" msgid="8367540370912066757">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"ਕੀ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; \'ਤੇ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਲਈ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਪਹੁੰਚ ਨੂੰ ਬਦਲਣਾ ਹੈ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"ਕੀ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; \'ਤੇ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਲਈ ਕੈਮਰਾ ਪਹੁੰਚ ਨੂੰ ਬਦਲਣਾ ਹੈ?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; \'ਤੇ ਆਪਣੇ ਮਹੱਤਵਪੂਰਨ ਲੱਛਣਾਂ ਸੰਬੰਧੀ ਸੈਂਸਰ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ਐਪ ਦੀ ਵਰਤੋਂ ਨਾ ਕਰਨ ਵੇਲੇ ਵੀ, ਇਸ ਐਪ ਨੂੰ ਹਰ ਸਮੇਂ ਸਰੀਰ ਸੰਬੰਧੀ ਸੈਂਸਰ ਡਾਟੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੇਣ ਲਈ, "<annotation id="link">"ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ।"</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_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ਨੂੰ &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"ਕੀ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ਕੋਈ ਨਹੀਂ"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"ਪਿਛਲੇ\n24 ਘੰਟੇ"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"ਪਿਛਲੇ\n7 ਦਿਨ"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> ਫ਼ੀਸਦ"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ Android ਨਾਲ ਸੁਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ ਹੈ। ਕਿਉਂਕਿ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਤੁਹਾਡੇ ਡਾਟੇ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਕੀਤੀ ਜਾਂਦੀ ਹੈ, ਇਸ ਲਈ ਇਸ ਐਪ ਦੀ ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਵਰਤੋਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਸਥਿਤੀ ਪੱਟੀ ਜਾਂ ਤੁਹਾਡੇ ਪਰਦੇਦਾਰੀ ਸੰਬੰਧੀ ਡੈਸ਼ਬੋਰਡ \'ਤੇ ਨਹੀਂ ਦਿਸਦੀ।"</string>
<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>
@@ -520,18 +561,25 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਬੰਦ ਹੈ"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚ ਬੰਦ ਹੈ"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਬੰਦ ਹੈ"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"ਇੰਫ਼ੋਟੇਨਮੈਂਟ ਐਪਾਂ ਲਈ"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"ਲੋੜੀਂਦੀਆਂ ਐਪਾਂ ਲਈ"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ਤੁਹਾਡੀ ਕਾਰ ਦੇ ਨਿਰਮਾਤਾ ਵੱਲੋਂ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ"</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_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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> ਵਜੇ ਪਹੁੰਚ ਕੀਤੀ ਗਈ"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"<xliff:g id="TIME_DATE">%1$s</xliff:g> ਵਜੇ ਕੱਲ੍ਹ ਪਹੁੰਚ ਕੀਤੀ ਗਈ"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> ਨੂੰ <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ਵਜੇ ਪਹੁੰਚ ਕੀਤੀ ਗਈ"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ਤੁਹਾਡਾ ਇਕਹਰੀ-ਵਰਤੋਂ ਪਾਸਵਰਡ 132435 ਹੈ"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗਾਂ"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਫ਼ਿਲਹਾਲ ਇਹ ਸੈਟਿੰਗ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"ਐਪ ਨੂੰ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"ਐਪ ਨੇ ਸੰਵੇਦਨਸ਼ੀਲ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਨਿੱਜੀ ਅਤੇ ਵਿੱਤੀ ਜਾਣਕਾਰੀ ਜੋਖਮ ਵਿੱਚ ਪੈ ਸਕਦੀ ਹੈ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ਇਹ ਸੰਭਵ ਹੈ, ਕਿ ਐਪ ਇਸ ਪ੍ਰਤਿਬੰਧਿਤ ਇਜਾਜ਼ਤ ਤੋਂ ਬਿਨਾਂ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣ ਦੇ ਤਰੀਕੇ ਬਾਰੇ ਜਾਣੋ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ਐਪ ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"ਐਪ ਨੇ ਸੰਵੇਦਨਸ਼ੀਲ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਹੈ, ਜਿਸ ਨਾਲ ਤੁਹਾਡੀ ਨਿੱਜੀ ਅਤੇ ਵਿੱਤੀ ਜਾਣਕਾਰੀ ਜੋਖਮ ਵਿੱਚ ਪੈ ਸਕਦੀ ਹੈ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ਇਹ ਸੰਭਵ ਹੈ, ਕਿ ਐਪ ਇਨ੍ਹਾਂ ਪ੍ਰਤਿਬੰਧਿਤ ਇਜਾਜ਼ਤਾਂ ਤੋਂ ਬਿਨਾਂ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣ ਦੇ ਤਰੀਕੇ ਬਾਰੇ ਜਾਣੋ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ਐਪ ਨੂੰ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੱਤੀ ਗਈ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ਇਸ ਇਜਾਜ਼ਤ ਲਈ ਪਹੁੰਚ ਦੇਣ ਨਾਲ ਤੁਹਾਡੀ ਨਿੱਜੀ ਅਤੇ ਵਿੱਤੀ ਜਾਣਕਾਰੀ ਜੋਖਮ ਵਿੱਚ ਪੈ ਸਕਦੀ ਹੈ।<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ਇਹ ਸੰਭਵ ਹੈ, ਕਿ ਐਪ ਇਸ ਪ੍ਰਤਿਬੰਧਿਤ ਇਜਾਜ਼ਤ ਤੋਂ ਬਿਨਾਂ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣ ਦੇ ਤਰੀਕੇ ਬਾਰੇ ਜਾਣੋ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ਹੋਰ ਜਾਣੋ"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ਠੀਕ ਹੈ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਬੇਨਤੀ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ਇਹ ਐਪ ਵਧੀਕ ਇਜਾਜ਼ਤਾਂ ਦੀ ਬੇਨਤੀ ਕਰ ਰਹੀ ਹੈ, ਪਰ ਸਟ੍ਰੀਮਿੰਗ ਸੈਸ਼ਨ ਵਿੱਚ ਇਜਾਜ਼ਤਾਂ ਨਹੀਂ ਦਿੱਤੀਆਂ ਜਾ ਸਕਦੀਆਂ। ਪਹਿਲਾਂ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਇਜਾਜ਼ਤ ਦਿਓ।"</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-pl-v34/strings.xml b/PermissionController/res/values-pl-v34/strings.xml
index b69bf4c4f..201b9d1c7 100644
--- a/PermissionController/res/values-pl-v34/strings.xml
+++ b/PermissionController/res/values-pl-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Zarządzaj dostępem aplikacji do danych dotyczących zdrowia"</string>
<string name="location_settings" msgid="8863940440881290182">"Dostęp do lokalizacji"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Dotyczy aplikacji i usług. Jeśli wyłączysz to ustawienie, dane mikrofonu wciąż mogą być udostępniane podczas połączenia z numerem alarmowym"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Dotyczy aplikacji i usług"</string>
</resources>
diff --git a/PermissionController/res/values-pl-watch/strings.xml b/PermissionController/res/values-pl-watch/strings.xml
index 1fbfdf245..0fe2421f6 100644
--- a/PermissionController/res/values-pl-watch/strings.xml
+++ b/PermissionController/res/values-pl-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nie można zmienić"</string>
<string name="generic_yes" msgid="2489207724988649846">"Tak"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Anuluj"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Cały czas"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Gdy używasz aplikacji"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Cały czas"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Gdy używasz aplikacji"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Cały czas"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Gdy używasz aplikacji"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Cały czas"</string>
</resources>
diff --git a/PermissionController/res/values-pl/strings.xml b/PermissionController/res/values-pl/strings.xml
index a41e7459d..504f9a0a0 100644
--- a/PermissionController/res/values-pl/strings.xml
+++ b/PermissionController/res/values-pl/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"uprawnienia"</string>
<string name="cancel" msgid="8943320028373963831">"Anuluj"</string>
<string name="back" msgid="6249950659061523680">"Wstecz"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Odblokowany"</string>
<string name="blocked" msgid="9195547604866033708">"Zablokowany"</string>
<string name="on" msgid="280241003226755921">"Włączona"</string>
@@ -34,6 +36,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 +63,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 +74,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>
@@ -114,8 +118,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Wszystkie uprawnienia"</string>
<string name="other_permissions" msgid="2901186127193849594">"Inne funkcje aplikacji"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Prośba o pozwolenie"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear nie obsługuje instalowania ani odinstalowywania."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Wybierz, jakie uprawnienia dostępu ma mieć &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikacja &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; została zaktualizowana. Wybierz dla niej uprawnienia dostępu."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Anuluj"</string>
@@ -191,12 +193,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Zawsze zezwalaj na wszystko"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Zawsze pytaj"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nie zezwalaj"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Zezwól na ograniczony dostęp"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Dokładna lokalizacja"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Przybliżona lokalizacja"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Używaj dokładnej lokalizacji"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kiedy dokładna lokalizacja jest wyłączona, aplikacje mają dostęp do Twojej przybliżonej lokalizacji"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> – dostęp"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Dostęp tej aplikacji do: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Aplikacja ma przyznane uprawnienie <xliff:g id="PERM">%1$s</xliff:g> na tym urządzeniu: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Zobacz wszystkie uprawnienia aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Wyświetl wszystkie aplikacje z tym uprawnieniem"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Pokaż użycie mikrofonu w Asystencie"</string>
@@ -204,7 +208,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Usuń uprawnienia, jeśli aplikacja jest nieużywana"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Usuń uprawnienia i zwolnij miejsce"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Wstrzymuj aktywność w aplikacji, jeśli jest nieużywana"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Zarządzaj nieużywanymi aplikacjami"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Usuń uprawnienia i pliki tymczasowe oraz zatrzymaj powiadomienia"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Usuń uprawnienia i pliki tymczasowe, zatrzymaj powiadomienia i zarchiwizuj aplikację"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Aby chronić Twoje dane, usuniemy uprawnienia tej aplikacji, jeśli nie była używana od kilku miesięcy."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Aby chronić Twoje dane, jeśli aplikacja nie będzie używana przez kilka miesięcy, usuniemy te uprawnienia: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Aby chronić Twoje dane, usunęliśmy uprawnienia aplikacji, których nie używano od kilku miesięcy."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Zezwolono na zarządzanie wszystkimi plikami"</string>
<string name="ask_header" msgid="2633816846459944376">"Zawsze pytaj"</string>
<string name="denied_header" msgid="903209608358177654">"Nie mają dostępu"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> na tym urządzeniu: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Zobacz więcej aplikacji z dostępem do wszystkich plików"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dzień}few{# dni}many{# dni}other{# dnia}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# godzina}few{# godziny}many{# godzin}other{# godziny}}"</string>
@@ -340,7 +347,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 +369,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>
@@ -401,6 +408,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikacja do notatek"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikacje umożliwiające robienie notatek na urządzeniu"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notatki"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Domyślna aplikacja portfela"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikacja portfela cyfrowego"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aby ułatwić sobie wykonywanie różny form transakcji, w aplikacjach portfeli cyfrowych można przechowywać karty kredytowe i lojalnościowe, kluczyki do samochodu i inne rzeczy."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Ustawić aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> jako domyślną aplikację portfela cyfrowego?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nie potrzebuje uprawnień"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Bieżąca aplikacja domyślna"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Nie pytaj ponownie"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Ustaw jako domyślną"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Więcej ustawień domyślnych"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Otwieranie linków"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Domyślne do pracy"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Domyślne dla przestrzeni prywatnej"</string>
<string name="default_app_none" msgid="9084592086808194457">"Brak"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Domyślna aplikacja systemowa)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Brak aplikacji"</string>
@@ -455,48 +468,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Pokaż wykrywanie wyzwalacza asystenta"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Pokaż ikonę na pasku stanu, gdy używany jest mikrofon do uruchomienia asystenta głosowego"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć i multimediów na urządzeniu?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć i multimediów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do kontaktów?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do kontaktów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do lokalizacji urządzenia?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do lokalizacji urządzenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacja będzie mieć dostęp do lokalizacji tylko wtedy, gdy będzie używana"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do lokalizacji urządzenia?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do lokalizacji urządzenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Aplikacja chce mieć stały dostęp do Twojej lokalizacji, nawet gdy nie jest używana. "<annotation id="link">"Zezwól w ustawieniach"</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Zmienić uprawnienia dostępu do lokalizacji w przypadku aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>”?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Zmienić dostęp aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; do lokalizacji na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Aplikacja chce mieć stały dostęp do Twojej lokalizacji, nawet gdy nie jest używana. "<annotation id="link">"Zezwól w ustawieniach"</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na znajdowanie urządzeń w pobliżu, ustalanie ich względnego położenia i łączenie się z nimi?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na znajdowanie urządzeń w pobliżu, ustalanie ich względnego położenia i łączenie się z nimi na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na znajdowanie urządzeń w pobliżu, ustalanie ich względnego położenia i łączenie się z nimi? "<annotation id="link">"Zezwól w ustawieniach."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Zmienić dostęp aplikacji <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> do lokalizacji przybliżonej na dostęp do lokalizacji dokładnej?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Zmienić dostęp aplikacji <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> do lokalizacji urządzenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; z przybliżonej na dokładną?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Zezwolić aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” na dostęp do przybliżonej lokalizacji urządzenia?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do przybliżonej lokalizacji urządzenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Dokładna"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Przybliżona"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do kalendarza?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do kalendarza na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na wysyłanie i wyświetlanie SMS-ów?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na wysyłanie i wyświetlanie SMS-ów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć, multimediów i plików na urządzeniu?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć, multimediów i plików na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć, filmów, muzyki i dźwięków na tym urządzeniu?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć, filmów, muzyki, dźwięków i innych plików na tym urządzeniu?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do muzyki i innych plików audio na tym urządzeniu?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do muzyki i innych plików audio na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć i filmów na tym urządzeniu?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do zdjęć i filmów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do kolejnych zdjęć i filmów na tym urządzeniu?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do większej liczby zdjęć i filmów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na nagrywanie dźwięku?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na nagrywanie dźwięku na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacja będzie mogła nagrywać dźwięk tylko wtedy, gdy będzie używana"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Zezwolić aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” na nagrywanie dźwięku?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na nagrywanie dźwięku na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ta aplikacja chce móc nagrywać dźwięk przez cały czas, nawet gdy jej nie używasz. "<annotation id="link">"Zezwól w ustawieniach."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Zmienić uprawnienia dostępu do mikrofonu w przypadku aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>”?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Zmienić dostęp aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; do mikrofonu na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ta aplikacja chce móc nagrywać dźwięk przez cały czas, nawet gdy jej nie używasz. "<annotation id="link">"Zezwól w ustawieniach."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do aktywności fizycznej?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do aktywności fizycznej na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na robienie zdjęć i nagrywanie filmów?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na robienie zdjęć i nagrywanie filmów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacja będzie mogła robić zdjęcia i nagrywać filmy tylko wtedy, gdy będzie używana"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Zezwolić aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>” na robienie zdjęć i nagrywanie filmów?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na robienie zdjęć i nagrywanie filmów na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ta aplikacja chce móc robić zdjęcia i nagrywać filmy przez cały czas, nawet gdy jej nie używasz. "<annotation id="link">"Zezwól w ustawieniach."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Zmienić uprawnienia dostępu do aparatu w przypadku aplikacji „<xliff:g id="APP_NAME">%1$s</xliff:g>”?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Zmienić dostęp aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; do aparatu na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ta aplikacja chce móc robić zdjęcia i nagrywać filmy przez cały czas, nawet gdy jej nie używasz. "<annotation id="link">"Zezwól w ustawieniach."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do rejestrów połączeń?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do rejestrów połączeń na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na wykonywanie połączeń telefonicznych i zarządzanie nimi?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na nawiązywanie połączeń telefonicznych i zarządzanie nimi na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do danych z czujnika dotyczących parametrów życiowych na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp do danych z czujnika dotyczących parametrów życiowych na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Nadal zezwalać aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na dostęp na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; do danych z czujników na ciele, gdy jest ona używana?"</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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Zezwolić aplikacji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na wysyłanie powiadomień na urządzeniu &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organizacja zezwala na dostęp aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g> do Twojej lokalizacji"</string>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Brak"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Ostatnie\n24 godziny"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Ostatnie\n7 dni"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> procent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest chroniona przez Androida. Ponieważ Twoje dane są przetwarzane na tym urządzeniu, używanie uprawnień przez tę aplikację nie jest wyświetlane na pasku stanu w panelu prywatności."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest chroniona przez Androida. Ponieważ Twoje dane są przetwarzane na tym urządzeniu, używanie uprawnień przez tę aplikację nie jest wyświetlane w panelu prywatności."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Aparat urządzenia jest zablokowany"</string>
@@ -520,6 +561,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"dla aplikacji i usług"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Dane z mikrofonu mogą być udostępniane, kiedy dzwonisz na numer alarmowy."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Zmień"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Dostęp do aparatu jest wyłączony"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Dostęp do mikrofonu jest wyłączony"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Dostęp do lokalizacji jest wyłączony"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Dla aplikacji informacyjno-rozrywkowych"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Dla wymaganych aplikacji"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ta aplikacja jest wymagana"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ta aplikacja jest wymagana przez producenta samochodu"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Bezpieczeństwo i prywatność"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Przeskanuj urządzenie"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Odrzuć"</string>
@@ -619,4 +667,26 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Zmiany w udostępnianiu danych"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Niektóre aplikacje zmieniły sposób udostępniania Twoich danych o lokalizacji"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Ustawienia"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Ostatni dostęp <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Ostatni dostęp wczoraj, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Ostatni dostęp <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Twoje hasło jednorazowe to 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ustawienie z ograniczonym dostępem"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ze względów bezpieczeństwa to ustawienie jest obecnie niedostępne."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikacja nie otrzymała dostępu do uprawnień <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacja wymaga dostępu do uprawnień newralgicznych, co może zagrażać Twoim danym osobowym i informacjom finansowym.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Możliwe, że bez tego uprawnienia z ograniczeniami aplikacja nie będzie działać poprawnie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak zezwolić na dostęp&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikacja nie otrzymała dostępu jako domyślna <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacja wymaga dostępu do uprawnień newralgicznych, co może zagrażać Twoim danym osobowym i informacjom finansowym.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Możliwe, że bez tego uprawnienia z ograniczeniami aplikacja nie będzie działać poprawnie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak zezwolić na dostęp&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikacja nie otrzymała dostępu"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Dostęp do tych uprawnień może zagrażać Twoim danym osobowym i informacjom finansowym.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Możliwe, że bez tych uprawnień z ograniczeniami aplikacja nie będzie działać poprawnie. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Jak zezwolić na dostęp&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Więcej informacji"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Prośba o uprawnienia została zablokowana"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ta aplikacja prosi o dodatkowe uprawnienia, ale nie można ich przyznać w trakcie sesji strumieniowania. Najpierw przyznaj te uprawnienia na telefonie."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-pt-rBR-v34/strings.xml b/PermissionController/res/values-pt-rBR-v34/strings.xml
index 6e77e0432..28380d50b 100644
--- a/PermissionController/res/values-pt-rBR-v34/strings.xml
+++ b/PermissionController/res/values-pt-rBR-v34/strings.xml
@@ -17,11 +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">"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>
<string name="location_settings" msgid="8863940440881290182">"Acesso ao local"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Para apps e serviços. Se esta configuração estiver desativada, os dados do microfone ainda poderão ser compartilhados quando você ligar para um número de emergência"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Para apps e serviços"</string>
</resources>
diff --git a/PermissionController/res/values-pt-rBR-watch/strings.xml b/PermissionController/res/values-pt-rBR-watch/strings.xml
index aa3b75772..84379daa0 100644
--- a/PermissionController/res/values-pt-rBR-watch/strings.xml
+++ b/PermissionController/res/values-pt-rBR-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Impossível alterar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sim"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancelar"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"O tempo todo"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Durante o uso do app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"O tempo todo"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Durante o uso do app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"O tempo todo"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Durante o uso do app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"O tempo todo"</string>
</resources>
diff --git a/PermissionController/res/values-pt-rBR/strings.xml b/PermissionController/res/values-pt-rBR/strings.xml
index 7bb03ff08..98b1aca1e 100644
--- a/PermissionController/res/values-pt-rBR/strings.xml
+++ b/PermissionController/res/values-pt-rBR/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permissões"</string>
<string name="cancel" msgid="8943320028373963831">"Cancelar"</string>
<string name="back" msgid="6249950659061523680">"Voltar"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Fechar"</string>
<string name="available" msgid="6007778121920339498">"Disponível"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqueado"</string>
<string name="on" msgid="280241003226755921">"Ativado"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Todas as permissões"</string>
<string name="other_permissions" msgid="2901186127193849594">"Outros recursos do app"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Solicitação de permissão"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"As ações de instalar/desinstalar não são compatíveis com o Android Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Escolha o que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; terá permissão para acessar"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"O app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foi atualizado. Escolha o que esse app terá permissão para acessar."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancelar"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Sempre permitir tudo"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permitir acesso limitado"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Local exato"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Local aproximado"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar local exato"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Apps podem acessar a localização aproximada quando o local exato está desativado"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Permissão para acessar <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Permitir que este app acesse: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Acesso de <xliff:g id="PERM">%1$s</xliff:g> para esse app no <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todas as permissões do app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Mostrar todos os apps que têm esta permissão"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostrar uso de microfone pelo Assistente"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Remover permissões se o app não for usado"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remover permissões e liberar espaço"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausar atividade no app quando não usado"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gerenciar o app fora de uso"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissões, exclui arquivos temporários e para notificações"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remover permissões, excluir arquivos temporários, parar notificações e arquivar o app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger seus dados, as permissões serão removidas se o app não for usado por alguns meses."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para proteger seus dados, se o app não for usado por alguns meses, as seguintes permissões serão removidas: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para proteger seus dados, as permissões de apps que não foram usados nos últimos meses foram removidas."</string>
@@ -221,7 +226,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,12 +251,13 @@
<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>
<string name="denied_header" msgid="903209608358177654">"Não permitido"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> no <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Veja mais apps que têm acesso a todos os arquivos"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}one{# dia}many{# dias}other{# dias}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}one{# hora}many{# de horas}other{# horas}}"</string>
@@ -356,14 +362,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"App de notas"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps que permitem a criação de notas no dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Carteira digital padrão"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"App de carteira"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Os apps de carteira digital armazenam seus cartões de crédito e de fidelidade, chaves de carros, entre outros itens, para agilizar suas transações."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como seu app de carteira padrão?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nenhuma permissão necessária"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Padrão atual"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Não perguntar novamente"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Definir como padrão"</string>
@@ -426,7 +437,8 @@
<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_apps_for_private_profile" msgid="2022024112144880785">"Padrão para o espaço privado"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostrar detecção de gatilho do assistente"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostrar ícone na barra de status quando o microfone for usado para ativar o assistente por voz"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e mídia no seu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e arquivos de mídia no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seus contatos?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seus contatos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"O app só terá acesso ao local enquanto estiver sendo usado"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização deste dispositivo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Este app pode querer 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="permgroupupgraderequest_location" msgid="8328408946822691636">"Mudar o acesso que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem à localização?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Mudar o acesso que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem ao local no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, se conecte e saiba o posicionamento relativo de dispositivos por perto no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"Mudar o acesso ao local do <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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_device_aware_coarselocation" msgid="8367540370912066757">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse o local aproximado do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;’s?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exata"</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; acesse sua agenda?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse sua agenda no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse e envie mensagens SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie e acesse mensagens SMS no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos, mídia e arquivos no seu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos, mídia e arquivos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse &lt;b&gt;fotos, vídeos, músicas e áudios&lt;/b&gt; neste dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse &lt;b&gt;fotos, vídeos, músicas, áudios e outros arquivos&lt;/b&gt; neste dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse músicas e áudios neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse músicas e áudios no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e vídeos neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse mais fotos e vídeos neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse mais fotos e vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"O app poderá gravar áudio apenas quando estiver em uso"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Este app pode querer gravar áudio a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Mudar o acesso que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem ao microfone?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Mudar o acesso que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem ao microfone no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Este app quer gravar áudio a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse sua atividade física?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse os dados de atividade física no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"O app poderá tirar fotos e gravar vídeos apenas quando estiver em 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; tire fotos e grave vídeos?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Este app pode querer tirar fotos e gravar vídeos a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Mudar o acesso que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem à câmera?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Mudar o acesso que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem à câmera no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Este app quer tirar fotos e gravar vídeos a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seu registro de chamadas telefônicas?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seu registro de chamadas telefônicas no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gerencie e faça chamadas telefônicas?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faça e gerencie ligações telefônicas no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse os dados do sensor sobre seus sinais vitais?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse dados do sensor sobre seus sinais vitais no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Este app quer acessar os dados do sensor sobre seus sinais vitais o tempo todo, mesmo quando não estiver em uso. Para fazer essa mudança, "<annotation id="link">"acesse as configurações"</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; acesse os dados do sensor sobre seus sinais vitais?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse os dados do sensor sobre seus sinais vitais no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para permitir que o app tenha acesso a dados do sensor corporal a qualquer momento, mesmo quando não estiver em uso, "<annotation id="link">"acesse as configurações"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Continuar permitindo que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse dados do sensor corporal enquanto estiver em uso?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Continuar permitindo que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse dados do sensor corporal no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; durante o uso do app?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie notificações?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie notificações para você no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Permissões controladas"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> tem acesso à localização"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Sua organização permite que o app <xliff:g id="APP_NAME">%1$s</xliff:g> tenha acesso à sua localização"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nenhuma"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Últimas\n24 horas"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Nos últimos\nsete dias"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> por cento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é Protegido pelo Android. Como seus dados são processados no dispositivo, o uso da permissão do app não é mostrado na barra de status nem no painel de privacidade."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é Protegido pelo Android. Como seus dados são processados no dispositivo, o uso da permissão do app não é mostrado no painel de privacidade."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"A câmera do dispositivo está bloqueada"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para apps e serviços"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Os dados do microfone ainda poderão ser compartilhados quando você ligar para um número de emergência."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Mudar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"O acesso à câmera está desativado"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"O acesso ao microfone está desativado"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"O acesso à localização está desativado"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para apps de infoentretenimento"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para apps necessários"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"O app é necessário"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"O app é exigido pelo fabricante do carro"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Segurança e privacidade"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Verificar dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dispensar"</string>
@@ -608,8 +655,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Atualizações do compartilhamento de dados"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Alguns apps mudaram a forma como podem compartilhar seus dados de local"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Configurações"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Último acesso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acesso: ontem, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acesso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Sua senha única é 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Configuração restrita"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, essa configuração está indisponível no momento."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O app não recebeu a seguinte permissão de acesso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"O app solicitou acesso a uma permissão sensível que pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O app não recebeu o acesso para ser a escolha padrão de: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"O app solicitou acesso a permissões sensíveis que podem colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essas permissões restritas. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"O app não recebeu a permissão de acesso"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"O acesso a essa permissão pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saiba mais"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitação de permissão suprimida"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esse app está solicitando permissões extras, mas elas não podem ser concedidas em uma sessão de streaming. Dê permissão pelo smartphone primeiro."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para uma chamada ou mensagem de texto de emergência"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Localização enviada aos serviços de emergência"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"O app acessou a localização do seu dispositivo durante uma ligação ou mensagem de texto para um número de emergência. Isso pode acontecer mesmo quando o app não tem permissão de localização ou a localização do dispositivo está desativada. "<a href="https://support.google.com/android/answer/9319337">"Saiba mais"</a></string>
</resources>
diff --git a/PermissionController/res/values-pt-rPT-v34/strings.xml b/PermissionController/res/values-pt-rPT-v34/strings.xml
index 2f7ce1be2..a4d6bd83b 100644
--- a/PermissionController/res/values-pt-rPT-v34/strings.xml
+++ b/PermissionController/res/values-pt-rPT-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Faça a gestão do acesso de apps a dados de saúde"</string>
<string name="location_settings" msgid="8863940440881290182">"Acesso à localização"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"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="6846532794702613851">"Para apps e serviços"</string>
</resources>
diff --git a/PermissionController/res/values-pt-rPT-watch/strings.xml b/PermissionController/res/values-pt-rPT-watch/strings.xml
index 3c3a88806..60b1f5f4d 100644
--- a/PermissionController/res/values-pt-rPT-watch/strings.xml
+++ b/PermissionController/res/values-pt-rPT-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Impossív. alterar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sim"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancelar"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Sempre"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Quando usar a app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Sempre"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Quando usar a app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Sempre"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Quando usar a app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Sempre"</string>
</resources>
diff --git a/PermissionController/res/values-pt-rPT/strings.xml b/PermissionController/res/values-pt-rPT/strings.xml
index 012608eee..98fff8520 100644
--- a/PermissionController/res/values-pt-rPT/strings.xml
+++ b/PermissionController/res/values-pt-rPT/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"autorizações"</string>
<string name="cancel" msgid="8943320028373963831">"Cancelar"</string>
<string name="back" msgid="6249950659061523680">"Anterior"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Fechar"</string>
<string name="available" msgid="6007778121920339498">"Disponível"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqueado"</string>
<string name="on" msgid="280241003226755921">"Ativado"</string>
@@ -29,11 +30,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Todas as autorizações"</string>
<string name="other_permissions" msgid="2901186127193849594">"Outras capacidades de aplicações"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Pedido de autorização"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"As ações de instalar/desinstalar não são compatíveis com o Android Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Escolher a que conteúdos permite que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"O &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foi atualizado. Escolha a que conteúdos permite que esta app aceda."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancelar"</string>
@@ -191,20 +192,24 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Permitir sempre tudo"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permitir acesso limitado"</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>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Acesso a <xliff:g id="PERM">%1$s</xliff:g> para esta app no dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todas as autorizações da app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ver todas as apps com esta autorização"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostrar utilização do microfone do assistente"</string>
<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_label_v3" msgid="693340578642156657">"Gerir app se não for usada"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"Remover autorizações, eliminar ficheiros temporários e parar notificações"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remove autorizações, elimina ficheiros temporários, interrompe notificações e arquiva a app"</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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Com autorização para gerir todos os ficheiros"</string>
<string name="ask_header" msgid="2633816846459944376">"Perguntar sempre"</string>
<string name="denied_header" msgid="903209608358177654">"Não permitidas"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> no dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Veja mais apps que podem aceder a todos os ficheiros"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}many{# dias}other{# dias}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}many{# horas}other{# horas}}"</string>
@@ -347,9 +353,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 +363,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 +381,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"App de notas"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps que lhe permitem tirar notas no seu dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"App de carteira predefinida"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"App de carteira"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"As apps de carteira podem armazenar os seus cartões de crédito e de fidelidade, as chaves do carro, entre outras coisas, para ajudar em várias formas de transações."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Definir a app <xliff:g id="APP_NAME">%1$s</xliff:g> como a sua app de carteira predefinida?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Não são necessárias autorizações"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Predefinição atual"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Não perguntar novamente"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Definir como predef."</string>
@@ -426,13 +437,14 @@
<string name="default_apps_more" msgid="4078194675848858093">"Mais predefinições"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Abertura de links"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Predefinição para o trabalho"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Predefinição para espaço privado"</string>
<string name="default_app_none" msgid="9084592086808194457">"Nenhuma"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Predefinição do sistema)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Sem apps"</string>
<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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostre a deteção do acionador do assistente"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostre o ícone na barra de estado quando o microfone é utilizado para ativar o assistente de voz"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos e multimédia no dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos e multimédia no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos seus contactos?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos seus contactos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"A app tem acesso à localização apenas enquanto a estiver a utilizar"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização deste dispositivo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Esta app poderá pretender aceder sempre à sua localização, mesmo quando não a estiver a utilizar. "<annotation id="link">"Permita-o nas definições."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Quer alterar o acesso à localização para a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Alterar o acesso à localização para a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Esta app quer aceder sempre à sua localização, mesmo quando não a estiver a utilizar. "<annotation id="link">"Permita-o nas definiçõ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, determine a posição relativa dos dispositivos próximos e se ligue aos mesmos?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, ligue e determine a posição relativa de dispositivos próximos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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, determine a posição relativa dos dispositivos próximos e se ligue aos mesmos? "<annotation id="link">"Permita nas Definições."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Alterar o acesso à localização da app <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> de aproximada para exata?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Alterar o acesso à localização para a app <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; de aproximada para exata?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização aproximada deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à localização aproximada do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exata"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aproximada"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda ao calendário?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda ao seu calendário no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie e veja mensagens SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie e veja mensagens SMS no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos, multimédia e ficheiros no dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos, multimédia e ficheiros no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a &lt;b&gt;fotos, vídeos, música e áudio&lt;/b&gt; neste dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a &lt;b&gt;fotos, vídeos, música, áudio, etc.&lt;/b&gt; neste dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a música e áudio neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a música e áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos e vídeos neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a fotos e vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a mais fotos e vídeos neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a mais fotos e vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"A app apenas poderá gravar áudio enquanto a estiver a utilizar."</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Esta app pode pretender gravar áudio sempre, mesmo quando não a está a utilizar. "<annotation id="link">"Permita-o nas Definições."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Quer alterar o acesso ao microfone para a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Alterar o acesso ao microfone para a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Esta app quer gravar áudio sempre, mesmo quando não a está a utilizar. "<annotation id="link">"Permita-o nas Definições."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à sua atividade física?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda à sua atividade física no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeo?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"A app apenas poderá tirar fotos e gravar vídeos enquanto a estiver a utilizar."</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Esta app pode pretender 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="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Alterar o acesso à câmara para a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos registos de chamadas telefónicas no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"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 no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos dados de sensores sobre os seus sinais vitais no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos dados de sensores sobre os seus sinais vitais no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para permitir que esta app aceda sempre aos dados de sensores de corpo, mesmo quando não está a usá-la, "<annotation id="link">"aceda às definições."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Continuar a permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda aos dados de sensores de corpo enquanto usa a app?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Continuar a permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; aceda a dados de sensores de corpo no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; quando a usa?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lhe envie notificações?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Permitir que a app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lhe envie notificações no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Autorizações controladas"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> tem acesso à localização"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"A sua organização permite que a app <xliff:g id="APP_NAME">%1$s</xliff:g> aceda à sua localização"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nenhuma"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Últimas\n24 horas"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Últimos\n7 dias"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>: <xliff:g id="PERCENT">%2$d</xliff:g> por cento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está protegida pelo Android. Uma vez que os seus dados são processados neste dispositivo, a utilização da autorização desta app não é apresentada na barra de estado ou no seu painel de privacidade."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está protegida pelo Android. Uma vez que os seus dados são processados neste dispositivo, a utilização da autorização desta app não é apresentada no seu painel de privacidade."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"A câmara do dispositivo está bloqueada"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para apps e serviços"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Os dados do microfone ainda podem ser partilhados quando ligar para um número de emergência."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Alterar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"O acesso à câmara está desativado"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"O acesso ao microfone está desativado"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"O acesso à localização está desativado"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para apps de infoentretenimento"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para apps necessárias"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Esta app é necessária"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Esta app é exigida pelo fabricante do carro"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Segurança e privacidade"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Analisar dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Ignorar"</string>
@@ -583,9 +630,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 +655,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Atualizações da partilha de dados"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Algumas apps alteraram a forma como podem partilhar os seus dados de localização"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Definições"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Acedido à(s) <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Acedido ontem à(s) <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Acedido a <xliff:g id="TIME_DATE_0">%1$s</xliff:g> à(s) <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"A sua palavra-passe de utilização única é 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Definição restrita"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, esta definição está indisponível atualmente."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O acesso a <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> foi negado à app"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"A app pediu acesso a uma autorização confidencial que pode pôr em risco as suas informações pessoais e financeiras.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que a app não funcione corretamente sem esta autorização restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saiba como permitir o acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O acesso à função predefinida <xliff:g id="ROLE_NAME">%1$s</xliff:g> foi negado à app"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"A app pediu acesso a autorizações confidenciais que podem pôr em risco as suas informações pessoais e financeiras.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que a app não funcione corretamente sem estas autorizações restritas. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saiba como permitir o acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"O acesso foi negado à app"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"O acesso a esta autorização pode pôr em risco as suas informações pessoais e financeiras.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que a app não funcione corretamente sem esta autorização restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Saiba como permitir o acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saber mais"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Pedido de autorização suprimido"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esta app está a pedir autorizações adicionais, mas não é possível conceder autorizações numa sessão de streaming. Comece por conceder a autorização no seu telemóvel."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para chamada ou mensagem de texto de emergência"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Localização enviada aos serviços de emergência"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Esta app acedeu à localização do seu dispositivo durante uma chamada ou o envio de uma mensagem de texto para um número de emergência. Isto pode acontecer mesmo quando a app não tem autorização de acesso à localização ou a localização do dispositivo está desativada. "<a href="https://support.google.com/android/answer/9319337">"Saiba mais"</a></string>
</resources>
diff --git a/PermissionController/res/values-pt-v34/strings.xml b/PermissionController/res/values-pt-v34/strings.xml
index 6e77e0432..28380d50b 100644
--- a/PermissionController/res/values-pt-v34/strings.xml
+++ b/PermissionController/res/values-pt-v34/strings.xml
@@ -17,11 +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">"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>
<string name="location_settings" msgid="8863940440881290182">"Acesso ao local"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Para apps e serviços. Se esta configuração estiver desativada, os dados do microfone ainda poderão ser compartilhados quando você ligar para um número de emergência"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Para apps e serviços"</string>
</resources>
diff --git a/PermissionController/res/values-pt-watch/strings.xml b/PermissionController/res/values-pt-watch/strings.xml
index aa3b75772..84379daa0 100644
--- a/PermissionController/res/values-pt-watch/strings.xml
+++ b/PermissionController/res/values-pt-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Impossível alterar"</string>
<string name="generic_yes" msgid="2489207724988649846">"Sim"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Cancelar"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"O tempo todo"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Durante o uso do app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"O tempo todo"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Durante o uso do app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"O tempo todo"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Durante o uso do app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"O tempo todo"</string>
</resources>
diff --git a/PermissionController/res/values-pt/strings.xml b/PermissionController/res/values-pt/strings.xml
index 7bb03ff08..98b1aca1e 100644
--- a/PermissionController/res/values-pt/strings.xml
+++ b/PermissionController/res/values-pt/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permissões"</string>
<string name="cancel" msgid="8943320028373963831">"Cancelar"</string>
<string name="back" msgid="6249950659061523680">"Voltar"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Fechar"</string>
<string name="available" msgid="6007778121920339498">"Disponível"</string>
<string name="blocked" msgid="9195547604866033708">"Bloqueado"</string>
<string name="on" msgid="280241003226755921">"Ativado"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Todas as permissões"</string>
<string name="other_permissions" msgid="2901186127193849594">"Outros recursos do app"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Solicitação de permissão"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"As ações de instalar/desinstalar não são compatíveis com o Android Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Escolha o que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; terá permissão para acessar"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"O app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; foi atualizado. Escolha o que esse app terá permissão para acessar."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Cancelar"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Sempre permitir tudo"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Perguntar sempre"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Não permitir"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permitir acesso limitado"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Local exato"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Local aproximado"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Usar local exato"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Apps podem acessar a localização aproximada quando o local exato está desativado"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Permissão para acessar <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Permitir que este app acesse: <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Acesso de <xliff:g id="PERM">%1$s</xliff:g> para esse app no <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ver todas as permissões do app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Mostrar todos os apps que têm esta permissão"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Mostrar uso de microfone pelo Assistente"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Remover permissões se o app não for usado"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Remover permissões e liberar espaço"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausar atividade no app quando não usado"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gerenciar o app fora de uso"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Remove permissões, exclui arquivos temporários e para notificações"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Remover permissões, excluir arquivos temporários, parar notificações e arquivar o app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para proteger seus dados, as permissões serão removidas se o app não for usado por alguns meses."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para proteger seus dados, se o app não for usado por alguns meses, as seguintes permissões serão removidas: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para proteger seus dados, as permissões de apps que não foram usados nos últimos meses foram removidas."</string>
@@ -221,7 +226,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,12 +251,13 @@
<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>
<string name="denied_header" msgid="903209608358177654">"Não permitido"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> no <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Veja mais apps que têm acesso a todos os arquivos"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dia}one{# dia}many{# dias}other{# dias}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hora}one{# hora}many{# de horas}other{# horas}}"</string>
@@ -356,14 +362,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"App de notas"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Apps que permitem a criação de notas no dispositivo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notas"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Carteira digital padrão"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"App de carteira"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Os apps de carteira digital armazenam seus cartões de crédito e de fidelidade, chaves de carros, entre outros itens, para agilizar suas transações."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Definir <xliff:g id="APP_NAME">%1$s</xliff:g> como seu app de carteira padrão?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nenhuma permissão necessária"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Padrão atual"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Não perguntar novamente"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Definir como padrão"</string>
@@ -426,7 +437,8 @@
<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_apps_for_private_profile" msgid="2022024112144880785">"Padrão para o espaço privado"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Mostrar detecção de gatilho do assistente"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Mostrar ícone na barra de status quando o microfone for usado para ativar o assistente por voz"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e mídia no seu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e arquivos de mídia no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seus contatos?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seus contatos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização deste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"O app só terá acesso ao local enquanto estiver sendo usado"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização deste dispositivo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse a localização do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Este app pode querer 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="permgroupupgraderequest_location" msgid="8328408946822691636">"Mudar o acesso que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem à localização?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Mudar o acesso que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem ao local no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; encontre, se conecte e saiba o posicionamento relativo de dispositivos por perto no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_fineupgrade" msgid="4453775952305587571">"Mudar o acesso ao local do <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; 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_device_aware_coarselocation" msgid="8367540370912066757">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse o local aproximado do &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;’s?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exata"</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; acesse sua agenda?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse sua agenda no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse e envie mensagens SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie e acesse mensagens SMS no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos, mídia e arquivos no seu dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos, mídia e arquivos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse &lt;b&gt;fotos, vídeos, músicas e áudios&lt;/b&gt; neste dispositivo?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse &lt;b&gt;fotos, vídeos, músicas, áudios e outros arquivos&lt;/b&gt; neste dispositivo?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse músicas e áudios neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse músicas e áudios no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e vídeos neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse fotos e vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse mais fotos e vídeos neste dispositivo?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse mais fotos e vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"O app poderá gravar áudio apenas quando estiver em uso"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; grave áudio no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Este app pode querer gravar áudio a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Mudar o acesso que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem ao microfone?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Mudar o acesso que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem ao microfone no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Este app quer gravar áudio a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse sua atividade física?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse os dados de atividade física no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"O app poderá tirar fotos e gravar vídeos apenas quando estiver em 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; tire fotos e grave vídeos?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tire fotos e grave vídeos no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Este app pode querer tirar fotos e gravar vídeos a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Mudar o acesso que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem à câmera?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Mudar o acesso que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tem à câmera no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Este app quer tirar fotos e gravar vídeos a qualquer momento, mesmo quando não estiver em uso. "<annotation id="link">"Permita nas configurações."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seu registro de chamadas telefônicas?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse seu registro de chamadas telefônicas no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gerencie e faça chamadas telefônicas?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; faça e gerencie ligações telefônicas no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Permitir que &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse os dados do sensor sobre seus sinais vitais?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse dados do sensor sobre seus sinais vitais no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Este app quer acessar os dados do sensor sobre seus sinais vitais o tempo todo, mesmo quando não estiver em uso. Para fazer essa mudança, "<annotation id="link">"acesse as configurações"</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; acesse os dados do sensor sobre seus sinais vitais?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse os dados do sensor sobre seus sinais vitais no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para permitir que o app tenha acesso a dados do sensor corporal a qualquer momento, mesmo quando não estiver em uso, "<annotation id="link">"acesse as configurações"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Continuar permitindo que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse dados do sensor corporal enquanto estiver em uso?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Continuar permitindo que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; acesse dados do sensor corporal no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; durante o uso do app?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Permitir que o app &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie notificações?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Permitir que o &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envie notificações para você no &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Permissões controladas"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> tem acesso à localização"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Sua organização permite que o app <xliff:g id="APP_NAME">%1$s</xliff:g> tenha acesso à sua localização"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Nenhuma"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Últimas\n24 horas"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Nos últimos\nsete dias"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> por cento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é Protegido pelo Android. Como seus dados são processados no dispositivo, o uso da permissão do app não é mostrado na barra de status nem no painel de privacidade."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é Protegido pelo Android. Como seus dados são processados no dispositivo, o uso da permissão do app não é mostrado no painel de privacidade."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"A câmera do dispositivo está bloqueada"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para apps e serviços"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Os dados do microfone ainda poderão ser compartilhados quando você ligar para um número de emergência."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Mudar"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"O acesso à câmera está desativado"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"O acesso ao microfone está desativado"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"O acesso à localização está desativado"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para apps de infoentretenimento"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para apps necessários"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"O app é necessário"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"O app é exigido pelo fabricante do carro"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Segurança e privacidade"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Verificar dispositivo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Dispensar"</string>
@@ -608,8 +655,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Atualizações do compartilhamento de dados"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Alguns apps mudaram a forma como podem compartilhar seus dados de local"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Configurações"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Último acesso: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Último acesso: ontem, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Último acesso: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Sua senha única é 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Configuração restrita"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sua segurança, essa configuração está indisponível no momento."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"O app não recebeu a seguinte permissão de acesso: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"O app solicitou acesso a uma permissão sensível que pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"O app não recebeu o acesso para ser a escolha padrão de: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"O app solicitou acesso a permissões sensíveis que podem colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essas permissões restritas. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"O app não recebeu a permissão de acesso"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"O acesso a essa permissão pode colocar suas informações pessoais e financeiras em risco.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>É possível que o app não funcione corretamente sem essa permissão restrita. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Aprenda a conceder acesso&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Saiba mais"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitação de permissão suprimida"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Esse app está solicitando permissões extras, mas elas não podem ser concedidas em uma sessão de streaming. Dê permissão pelo smartphone primeiro."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para uma chamada ou mensagem de texto de emergência"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Localização enviada aos serviços de emergência"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"O app acessou a localização do seu dispositivo durante uma ligação ou mensagem de texto para um número de emergência. Isso pode acontecer mesmo quando o app não tem permissão de localização ou a localização do dispositivo está desativada. "<a href="https://support.google.com/android/answer/9319337">"Saiba mais"</a></string>
</resources>
diff --git a/PermissionController/res/values-ro-v34/strings.xml b/PermissionController/res/values-ro-v34/strings.xml
index e57e8c91a..cf7e275a1 100644
--- a/PermissionController/res/values-ro-v34/strings.xml
+++ b/PermissionController/res/values-ro-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Gestionează accesul aplicației la datele despre sănătate"</string>
<string name="location_settings" msgid="8863940440881290182">"Accesul la locație"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Pentru aplicații și servicii. Chiar dacă setarea este dezactivată, datele de la microfon pot fi trimise când apelezi un număr de urgență."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Pentru aplicații și servicii"</string>
</resources>
diff --git a/PermissionController/res/values-ro-watch/strings.xml b/PermissionController/res/values-ro-watch/strings.xml
index 2b5702c73..01b6310df 100644
--- a/PermissionController/res/values-ro-watch/strings.xml
+++ b/PermissionController/res/values-ro-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nu se poate modif"</string>
<string name="generic_yes" msgid="2489207724988649846">"Da"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Anulează"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Tot timpul"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Când folosești aplicația"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Tot timpul"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Când folosești aplicația"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Tot timpul"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Când folosești aplicația"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Tot timpul"</string>
</resources>
diff --git a/PermissionController/res/values-ro/strings.xml b/PermissionController/res/values-ro/strings.xml
index defbf3177..ab576d978 100644
--- a/PermissionController/res/values-ro/strings.xml
+++ b/PermissionController/res/values-ro/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"permisiuni"</string>
<string name="cancel" msgid="8943320028373963831">"Anulează"</string>
<string name="back" msgid="6249950659061523680">"Înapoi"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Închide"</string>
<string name="available" msgid="6007778121920339498">"Disponibil"</string>
<string name="blocked" msgid="9195547604866033708">"Blocat"</string>
<string name="on" msgid="280241003226755921">"Activat"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Toate permisiunile"</string>
<string name="other_permissions" msgid="2901186127193849594">"Alte funcții ale aplicației"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Solicitare de permisiune"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Acțiunile Instalează/Dezinstalează nu sunt acceptate pe Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Alege ce va putea accesa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplicația &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; a fost actualizată. Alege ce va putea accesa această aplicație."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Anulează"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Permite-le întotdeauna pe toate"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Întreabă de fiecare dată"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nu permite"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Permite cu acces limitat"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Locația exactă"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Locația aproximativă"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Folosește locația exactă"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Când locația exactă este dezactivată, aplicațiile îți pot accesa locația aproximativă"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Permisiune <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Accesul la <xliff:g id="PERM">%1$s</xliff:g> pentru această aplicație"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Acces de <xliff:g id="PERM">%1$s</xliff:g> pentru această aplicație pe <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Vezi toate permisiunile aplicației <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Vezi toate aplicațiile cu această permisiune"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Afișează datele de utilizare a microfonului cu Asistentul"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Elimină permisiunile dacă aplicația nu este folosită"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Revocă permisiunile și eliberează spațiu"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Întrerupe activitatea în aplicațiile nefolosite"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Gestionează aplicația dacă nu e folosită"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Revocă permisiunile, șterge fișierele temporare și oprește notificările"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Elimină permisiunile, șterge fișierele temporare, oprește notificările și arhivează aplicația"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Pentru a-ți proteja datele, se vor elimina permisiunile pentru această aplicație dacă nu este folosită câteva luni."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Pentru a-ți proteja datele, dacă aplicația nu este folosită câteva luni, se vor elimina următoarele permisiuni: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Pentru a-ți proteja datele, s-au eliminat permisiunile din aplicațiile pe care nu le-ai folosit de câteva luni."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Permisiunea de a gestiona toate fișierele"</string>
<string name="ask_header" msgid="2633816846459944376">"Întreabă de fiecare dată"</string>
<string name="denied_header" msgid="903209608358177654">"Nepermise"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> pe <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Vezi mai multe aplicații care pot accesa toate fișierele"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{O zi}few{# zile}other{# de zile}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# oră}few{# ore}other{# de ore}}"</string>
@@ -349,7 +355,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 +363,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplicația pentru note"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplicații cu ajutorul cărora poți să iei notițe pe dispozitiv"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notițe"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Aplicația portofel prestabilită"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplicație portofel"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplicațiile portofel pot să stocheze credit și carduri de fidelitate, chei de mașină și alte date, pentru a simplifica diferite tipuri de tranzacții."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Setezi <xliff:g id="APP_NAME">%1$s</xliff:g> ca aplicație portofel prestabilită?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nu sunt necesare permisiuni"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Aplicația prestabilită actuală"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Nu mai întreba"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Setează ca prestabilită"</string>
@@ -426,8 +437,9 @@
<string name="default_apps_more" msgid="4078194675848858093">"Mai multe setări prestabilite"</string>
<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_apps_for_private_profile" msgid="2022024112144880785">"Prestabilit pentru spațiul privat"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Afișează detectarea declanșării asistentului"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Afișează pictograma în bara de stare când microfonul este folosit pentru a activa asistentul vocal"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze fotografiile și conținutul media de pe dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la fotografiile și conținutul media de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți acceseze agenda?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze agenda ta de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația acestui dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația dispozitivului &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplicația va avea acces la locație doar când o folosești"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația acestui dispozitiv?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația dispozitivului &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"E posibil ca aplicația să dorească să-ți acceseze în permanență locația, chiar și când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Modifici accesul la locație pentru &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Modifici accesul la locație pentru &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Aplicația dorește să-ți acceseze în permanență locația, chiar și când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să găsească, să se conecteze la și să afle poziția relativă a dispozitivelor apropiate?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; poate să găsească, să se conecteze la, să determine poziția relativă a dispozitivelor apropiate pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să găsească, să se conecteze la și să determine poziția relativă a dispozitivelor apropiate? "<annotation id="link">"Permite în setări."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Schimbi permisiunile privind accesul la locație pentru <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> de la locația aproximativă la cea exactă?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Schimbi accesul la locația de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; pentru <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> de la aproximativă la exactă?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația aproximativă a acestui dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze locația aproximativă a dispozitivului &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exactă"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Aproximativă"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze calendarul?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți acceseze calendarul de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să trimită și să vadă mesaje SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să trimită și să vadă mesaje SMS de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze fotografiile, conținutul media și fișierele de pe dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze fotografii, conținut media și fișiere de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la &lt;b&gt;fotografii, clipuri, conținut audio și muzică&lt;/b&gt; de pe dispozitiv?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la &lt;b&gt;fotografii, clipuri, conținut audio, muzică și alte fișiere&lt;/b&gt; de pe dispozitiv?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la muzică și fișiere audio de pe acest dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Permiți accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la muzică și conținut audio de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze fotografiile și videoclipurile de pe dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze fotografii și videoclipuri de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze mai multe fotografii și videoclipuri de pe dispozitiv?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze mai multe fotografii și videoclipuri de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze conținut audio pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplicația va putea să înregistreze conținut audio doar când o folosești"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să înregistreze conținut audio pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Aplicația poate dori să înregistreze conținut audio permanent, chiar și când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Modifici accesul la microfon pentru &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Modifici accesul la microfon pentru &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Aplicația dorește să înregistreze conținut audio permanent, chiar și când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Permiți aplicației &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți acceseze activitatea fizică?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze activitatea ta fizică de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să fotografieze și să înregistreze video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să facă fotografii și să înregistreze videoclipuri pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplicația va putea să fotografieze și să înregistreze videoclipuri doar când o folosești"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să facă fotografii și să înregistreze videoclipuri?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să facă fotografii și să înregistreze videoclipuri pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Aplicația poate dori să fotografieze și să înregistreze videoclipuri permanent, chiar când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Modifici accesul la camera foto pentru &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Modifici accesul la camera foto pentru &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Aplicația dorește să fotografieze și să înregistreze videoclipuri permanent, chiar și când nu o folosești. "<annotation id="link">"Acordă această permisiune din setări."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți acceseze jurnalele de apeluri?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți acceseze jurnalele de apeluri telefonice de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să dea și să gestioneze apeluri telefonice?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să inițieze și să gestioneze apeluri telefonice pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze datele de la senzori despre semnele vitale?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze datele de la senzori despre semnele tale vitale de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplicația solicită acces permanent la datele de la senzori despre semnele vitale, chiar și când nu o folosești. Pentru a face această modificare, "<annotation id="link">"accesează setările"</annotation>"."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze datele de la senzori despre semnele vitale?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să acceseze datele de la senzori despre semnele tale vitale de pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Pentru a permite accesul permanent al aplicației la datele de la senzorii corporali, chiar și atunci când nu o folosești, "<annotation id="link">"accesează setările"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Permiți în continuare accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la datele de la senzorii corporali în timpul folosirii aplicației?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Permiți în continuare accesul &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; la datele de la senzorii corporali ai dispozitivului &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; în timpul folosirii aplicației?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți trimită notificări?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Permiți ca &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; să-ți trimită notificări pe &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Permisiuni controlate"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> are acces la locație"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organizația ta permite ca <xliff:g id="APP_NAME">%1$s</xliff:g> să-ți acceseze locația"</string>
@@ -512,6 +551,9 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Fără"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Ultimele\n24 de ore"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Ultimele\nșapte zile"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for privdash_usage_percent (6893824766124414127) -->
+ <skip />
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> este protejată de Android. Întrucât datele tale sunt procesate pe dispozitiv, folosirea permisiunilor de către aplicație nu apare în bara de stare sau în tabloul de bord de confidențialitate."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> este protejată de Android. Întrucât datele tale sunt procesate pe dispozitiv, folosirea permisiunilor de către aplicație nu apare în tabloul de bord de confidențialitate."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Camera dispozitivului este blocată"</string>
@@ -520,6 +562,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Pentru aplicații și servicii"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Datele de la microfon pot fi totuși trimise când suni la un număr de urgență."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Modifică"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Accesul la camera foto este dezactivat"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Accesul la microfon este dezactivat"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Accesul la locație este dezactivat"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Pentru aplicațiile de infotainment"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Pentru aplicațiile necesare"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Această aplicație este necesară"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Această aplicație este cerută de producătorul mașinii"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Securitate și confidențialitate"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Scanează dispozitivul"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Închide"</string>
@@ -619,4 +668,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Actualizări privind permiterea accesului la date"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Unele aplicații au schimbat modul în care pot permite accesul la datele tale privind locațiile"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Setări"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Data accesării: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Data accesării: ieri, <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Data accesării: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Parola ta unică este 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Setare restricționată"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Pentru securitatea ta, setarea este momentan indisponibilă."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Accesul aplicației la <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> a fost refuzat"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplicația a solicitat acces la o permisiune de accesare a informațiilor sensibile care îți poate pune în pericol informațiile financiare și cu caracter personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Este posibil ca aplicația să nu funcționeze corect fără această permisiune restricționată. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Află cum să permiți accesul&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Accesul aplicației pentru a deveni <xliff:g id="ROLE_NAME">%1$s</xliff:g> prestabilită a fost refuzat"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplicația a solicitat acces la permisiuni de accesare a informațiilor sensibile care îți pot pune în pericol informațiile financiare și cu caracter personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Este posibil ca aplicația să nu funcționeze corect fără aceste permisiuni restricționate. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Află cum să permiți accesul&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Accesul aplicației a fost refuzat"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Accesul la această permisiune îți poate pune în pericol informațiile financiare și cu caracter personal.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Este posibil ca aplicația să nu funcționeze corect fără această permisiune restricționată. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Află cum să permiți accesul&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Află mai multe"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Solicitarea de permisiune s-a suprimat"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Aplicația solicită permisiuni suplimentare, dar acestea nu pot fi acordate într-o sesiune de streaming. Acordă permisiunea întâi pe telefon."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Pentru apel sau mesaj de urgență"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Locație trimisă la serviciile de urgență"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Această aplicație a accesat locația dispozitivului când ai inițiat un apel sau ai trimis un mesaj către un număr de urgență. Se poate întâmpla chiar dacă aplicația nu are permisiune pentru locație sau locația dispozitivului este dezactivată."<a href="https://support.google.com/android/answer/9319337">"Află mai mult"</a></string>
</resources>
diff --git a/PermissionController/res/values-ru-v34/strings.xml b/PermissionController/res/values-ru-v34/strings.xml
index 64a927b69..a16586fc0 100644
--- a/PermissionController/res/values-ru-v34/strings.xml
+++ b/PermissionController/res/values-ru-v34/strings.xml
@@ -17,11 +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">"Здоровье и спорт"</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>
</resources>
diff --git a/PermissionController/res/values-ru-watch/strings.xml b/PermissionController/res/values-ru-watch/strings.xml
index a39697ff2..c7f4fd885 100644
--- a/PermissionController/res/values-ru-watch/strings.xml
+++ b/PermissionController/res/values-ru-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Нельзя изменить"</string>
<string name="generic_yes" msgid="2489207724988649846">"Да"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Отмена"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Всегда"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"При использовании"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Всегда"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"При использовании"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Всегда"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"При использовании"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Всегда"</string>
</resources>
diff --git a/PermissionController/res/values-ru/strings.xml b/PermissionController/res/values-ru/strings.xml
index 3c8d4ea58..4fa0397dc 100644
--- a/PermissionController/res/values-ru/strings.xml
+++ b/PermissionController/res/values-ru/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"разрешения"</string>
<string name="cancel" msgid="8943320028373963831">"Отмена"</string>
<string name="back" msgid="6249950659061523680">"Назад"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Доступно"</string>
<string name="blocked" msgid="9195547604866033708">"Заблокировано"</string>
<string name="on" msgid="280241003226755921">"Включено"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<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">"Wear OS"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Установка и удаление не поддерживаются в Wear OS."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Выберите разрешения для приложения &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Приложение &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; обновлено. Выберите разрешения для него."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Отмена"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"Разрешить ограниченный доступ"</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>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Разрешение \"<xliff:g id="PERM">%1$s</xliff:g>\" для этого приложения на устройстве \"<xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"Приостановить работу в неактивный период"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Удалить разрешения и временные файлы, прекратить отправку уведомлений"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Удалить разрешения и временные файлы, прекратить отправку уведомлений и перенести приложение в архив"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Для защиты ваших данных мы отзовем разрешения, предоставленные этому приложению, если вы не будете пользоваться им несколько месяцев."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Если вы не будете пользоваться приложением несколько месяцев, в целях защиты ваших данных мы отзовем следующие разрешения: <xliff:g id="PERMS">%1$s</xliff:g>."</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"В целях защиты ваших данных мы отозвали разрешения для приложений, которыми вы не пользовались несколько месяцев."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Приложения с доступом ко всем файлам"</string>
<string name="ask_header" msgid="2633816846459944376">"Спрашивать каждый раз"</string>
<string name="denied_header" msgid="903209608358177654">"Запрещено"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> на устройстве \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\""</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Ещё приложения с доступом ко всем файлам"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 день}one{# день}few{# дня}many{# дней}other{# дня}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# час}one{# час}few{# часа}many{# часов}other{# часа}}"</string>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Кошелек по умолчанию"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Приложение-кошелек"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"В приложении-кошельке можно хранить кредитные карты, карты постоянного клиента, автомобильные ключи и другие цифровые продукты. С его помощью удобнее проводить различные типы транзакций."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Использовать приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" как кошелек по умолчанию?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Разрешения не требуются."</string>
<string name="request_role_current_default" msgid="738722892438247184">"Используется по умолчанию"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Больше не спрашивать"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"По умолчанию"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Другие приложения по умолчанию"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Переход по ссылкам"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Стандартные для работы"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Приложения по умолчанию для личного пространства"</string>
<string name="default_app_none" msgid="9084592086808194457">"Нет"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(по умолчанию)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Приложений нет"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к фото и мультимедиа на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к контактам на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным о местоположении устройства &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным о местоположении устройства &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Изменить для приложения &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; настройки доступа к данным о местоположении на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; находить устройства поблизости, подключаться к ним и определять их относительное местоположение на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"Разрешить приложению \"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\" доступ к данным о точном местоположении устройства &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным о приблизительном местоположении устройства &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к календарю на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; отправлять и просматривать SMS на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к фото, мультимедиа и файлам на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к музыке и аудио на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к фото и видео на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к другим фото и видео на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записывать аудио на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записывать аудио на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Изменить для приложения &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; настройки доступа к микрофону на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным о физической активности на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; делать снимки и записывать видео на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; делать снимки и записывать видео на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Изменить для приложения &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; настройки доступа к камере на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к списку вызовов на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; совершать телефонные звонки и управлять ими на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным нательных датчиков на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным нательных датчиков на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Чтобы предоставить приложению доступ к данным нательных датчиков, даже когда вы им не пользуетесь, "<annotation id="link">"измените настройки"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ к данным нательных датчиков на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Разрешить приложению &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; отправлять вам уведомления на устройстве &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,9 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Нет"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Последние\n24 часа"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Последние\n7 дней"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for privdash_usage_percent (6893824766124414127) -->
+ <skip />
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" защищено Android. Поскольку ваши данные обрабатываются на устройстве, разрешения, используемые приложением, не показываются в строке состояния на панели управления разрешениями."</string>
<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>
@@ -520,7 +563,14 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Доступ к камере запрещен"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Доступ к микрофону запрещен"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Доступ к геоданным отключен"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Для информационно-развлекательных приложений"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Для обязательных приложений"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Это обязательное приложение"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Это приложение указано производителем автомобиля как обязательное"</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 +581,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>
@@ -619,4 +669,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Последний доступ: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Последний доступ вчера: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Последний доступ: <xliff:g id="TIME_DATE_0">%1$s</xliff:g>, <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Ваш одноразовый код: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Настройки с ограниченным доступом"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"В целях безопасности эти настройки пока недоступны."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Для приложения заблокировано разрешение \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Приложение запрашивает разрешение на доступ к конфиденциальной информации. Если вы предоставите его, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без такого разрешения приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Запрещено использовать приложение в качестве стандартного в категории \"<xliff:g id="ROLE_NAME">%1$s</xliff:g>\""</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Приложение запрашивает разрешения на доступ к конфиденциальной информации. Если вы предоставите их, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без таких разрешений приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Доступ запрещен"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Если вы предоставите это разрешение, ваши личные и финансовые данные могут оказаться под угрозой.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без него приложение может работать неправильно. Узнайте, &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;как предоставить доступ к данным&lt;/a&gt;."</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Подробнее"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ОК"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Запрос разрешений заблокирован"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Это приложение запрашивает дополнительные разрешения, которые невозможно предоставить во время трансляции на устройство. Сначала откройте доступ на телефоне."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-si-v34/strings.xml b/PermissionController/res/values-si-v34/strings.xml
index 26121467a..a4c016704 100644
--- a/PermissionController/res/values-si-v34/strings.xml
+++ b/PermissionController/res/values-si-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-si-watch/strings.xml b/PermissionController/res/values-si-watch/strings.xml
index 48c5164f6..a6292c8c3 100644
--- a/PermissionController/res/values-si-watch/strings.xml
+++ b/PermissionController/res/values-si-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"වෙනස් කළ නොහැකිය"</string>
<string name="generic_yes" msgid="2489207724988649846">"ඔව්"</string>
<string name="generic_cancel" msgid="2631708607129269698">"අවලංගු කරන්න"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"සෑම විටම"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"යෙදුම භාවිතා කරන අතරේ"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"සෑම විටම"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"යෙදුම භාවිතා කරන අතරේ"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"සෑම විටම"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"යෙදුම භාවිතා කරන අතරේ"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"සෑම විටම"</string>
</resources>
diff --git a/PermissionController/res/values-si/strings.xml b/PermissionController/res/values-si/strings.xml
index 1899c6e70..c72db1460 100644
--- a/PermissionController/res/values-si/strings.xml
+++ b/PermissionController/res/values-si/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"අවසර"</string>
<string name="cancel" msgid="8943320028373963831">"අවලංගු කරන්න"</string>
<string name="back" msgid="6249950659061523680">"ආපසු යන්න"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"තිබේ"</string>
<string name="blocked" msgid="9195547604866033708">"අවහිරයි"</string>
<string name="on" msgid="280241003226755921">"සක්‍රීය"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear මත ස්ථාපන/අස්ථාපනය ක්‍රියා සහාය දක්වන්නේ නැත."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට පිවිසීමට ඉඩ දෙන දේ තෝරන්න"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; යාවත්කාලීන කර ඇත. මෙම යෙදුමට පිවිසීමට ඉඩ දෙන දේ තෝරන්න."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"අවලංගු කරන්න"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"සීමිත ප්‍රවේශයට ඉඩ දෙන්න"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> මත මෙම යෙදුම සඳහා <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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"යෙදුම භාවිතා නොකළේ නම් කළමනාකරණය කරන්න"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"අවසර ඉවත් කරන්න, තාවකාලික ගොනු මකන්න සහ දැනුම්දීම් නවත්වන්න"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"අවසර ඉවත් කරන්න, තාවකාලික ගොනු මකා දමන්න, දැනුම්දීම් නවත්වන්න, සහ යෙදුම ලේඛනාරක්ෂණය කරන්න"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"ඔබගේ දත්ත ආරක්ෂා කිරීම සඳහා, යෙදුම මාස කිහිපයක් භාවිතා නොකරන්නේ නම් මෙම යෙදුම සඳහා අවසර ඉවත් කරනු ලැබේ."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"ඔබගේ දත්ත ආරක්ෂා කිරීම සඳහා, යෙදුම මාස කිහිපයක් භාවිතා නොකරන්නේ නම් පහත අවසර ඉවත් කරනු ලැබේ: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ඔබේ දත්ත ආරක්ෂා කිරීමට, ඔබ මාස කීපයක් තුළ භාවිත කර නැති යෙදුම්වලින් අවසර ඉවත් කර ඇත"</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"සියලු පාලන කළමනාකරණය කිරීමට ඉඩ දී ඇත"</string>
<string name="ask_header" msgid="2633816846459944376">"සෑම විටම ඉල්ලන්න"</string>
<string name="denied_header" msgid="903209608358177654">"ඉඩ නොදේ"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> මත <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"සියලු ගොනු වෙත ප්‍රවේශ විය හැකි තව යෙදුම් බලන්න"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{දින 1ක්}one{දින #ක්}other{දින #ක්}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{පැය #ක්}one{පැය #ක්}other{පැය #ක්}}"</string>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"පෙරනිමි පසුම්බි යෙදුම"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"පසුම්බි යෙදුම"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"පසුම්බි යෙදුම්වලට විවිධ ආකාරයේ ගනුදෙනුවලට උදවු කිරීමට ඔබේ ණය සහ ලැදියා කාඩ්පත්, මෝටර් රථ යතුරු සහ වෙනත් දේවල් ගබඩා කළ හැක."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"ඔබේ පෙරනිමි පසුම්බි යෙදුම ලෙස <xliff:g id="APP_NAME">%1$s</xliff:g> සකසන්න ද?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"අවසර අවශ්‍ය නැත"</string>
<string name="request_role_current_default" msgid="738722892438247184">"වත්මන් පෙරනිමිය"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"නැවත නොඅසන්න"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"පෙරනිමි ලෙස සකසන්න"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"තවත් පෙරනිමියන්"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"සබැඳි විවෘත කිරීම"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"වැඩ සඳහා පෙරනිමි"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"රහසිගත අවකාශය සඳහා පෙරනිමිය"</string>
<string name="default_app_none" msgid="9084592086808194457">"කිසිවක් නැත"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(පද්ධතිය පෙරනිමි)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"යෙදුම් නොමැත"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; මත &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; සඳහා ස්ථාන ප්‍රවේශය වෙනස් කරන්නද?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"මෙම යෙදුමට ඔබ යෙදුම භාවිත නොකරමින් සිටින විට පවා සියලු අවස්ථාවල ඔබේ ස්ථානය වෙත ප්‍රවේශ වීමට අවශ්‍යයි. "<annotation id="link">"සැකසීම්වල ඉඩ දෙන්න."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;b&gt; හට අවට උපාංග සොයා ගැනීමට, සම්බන්ධ වීමට සහ ඒවායේ සාපේක්ෂ ස්ථානය සොයා ගැනීමට ඉඩ දෙන්නද?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; මත <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; මත &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; සඳහා මයික්‍රොෆෝන ප්‍රවේශය වෙනස් කරන්නද?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; මත &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; සඳහා කැමරා ප්‍රවේශය වෙනස් කරන්නද?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; මත ඔබේ ජීව සලකුණු පිළිබඳ සංවේදක දත්ත වෙත ප්‍රවේශ වීමට ඉඩ දෙන්නද?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"ඔබ යෙදුම භාවිත නොකරන විට පවා, මෙම යෙදුමට ශරීර සංවේදක දත්තවලට ප්‍රවේශ වීමට ඉඩ දීමට, "<annotation id="link">"සැකසීම් වෙත යන්න."</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_device_aware_sensors" msgid="5542771499929819675">"යෙදුම භාවිතයේ ඇති විට &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; හට &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"කිසිවක් නැත"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"පසුගිය\nපැය 24"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"පසුගිය\nදින 7"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> සියයට <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android මඟින් ආරක්ෂා කර ඇත. ඔබගේ දත්ත මෙම උපාංගයේ සකසා ඇති බැවින්, මෙම යෙදුමේ අවසර භාවිතය තත්ත්ව තීරුව හෝ ඔබගේ පෞද්ගලිකත්ව උපකරණ පුවරුව තුළ නොපෙන්වයි."</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"කැමරා ප්‍රවේශය ක්‍රියා විරහිතයි"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"මයික්‍රෆෝන ප්‍රවේශය ක්‍රියා විරහිතයි"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"ස්ථාන ප්‍රවේශය ක්‍රියා විරහිතයි"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"තොරතුරු විනෝදාස්වාද යෙදුම් සඳහා"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"අවශ්‍ය යෙදුම් සඳහා"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"මෙම යෙදුම අවශ්‍ය වේ"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"මෙම යෙදුම ඔබේ මෝටර් රථයේ නිෂ්පාදකයාට අවශ්‍ය වේ"</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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"ප්‍රවේශ වූයේ <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"ප්‍රවේශ වූයේ ඊයේ <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"ප්‍රවේශ වූයේ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ඔබේ එක් වරක මුරපදය 132435 වේ"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"සීමා කළ සැකසීම"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"ඔබේ ආරක්ෂාව සඳහා, මෙම සැකසීම දැනට නොමැත."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"යෙදුම <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> වෙත ප්‍රවේශය ප්‍රතික්ෂේප කරන ලදි"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"යෙදුම ඔබේ පුද්ගලික සහ මූල්‍ය තොරතුරු අවදානමට ලක් කළ හැකි සංවේදී අවසරයකට ප්‍රවේශය ඉල්ලා ඇත.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>මෙම සීමා කළ අවසරය නොමැතිව යෙදුම නිසි ලෙස ක්‍රියා නොකරනු ඇත. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ප්‍රවේශයට ඉඩ දෙන ආකාරය දැන ගන්න&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"යෙදුම පෙරනිමි <xliff:g id="ROLE_NAME">%1$s</xliff:g> වීමට ප්‍රවේශය ප්‍රතික්ෂේප කරන ලදි"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"යෙදුම ඔබේ පුද්ගලික සහ මූල්‍ය තොරතුරු අවදානමට ලක් කළ හැකි සංවේදී අවසර සඳහා ප්‍රවේශය ඉල්ලා ඇත.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>මෙම සීමා කළ අවසර නොමැතිව යෙදුම නිසි ලෙස ක්‍රියා නොකරනු ඇත. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ප්‍රවේශයට ඉඩ දෙන ආකාරය දැන ගන්න&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"යෙදුමට ප්‍රවේශය ප්‍රතික්ෂේප විය"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"මෙම අවසරයට ප්‍රවේශ වීම ඔබේ පුද්ගලික සහ මූල්‍ය තොරතුරු අවදානමට ලක් කළ හැක.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>මෙම සීමා කළ අවසරය නොමැතිව යෙදුම නිසි ලෙස ක්‍රියා නොකරනු ඇත. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ප්‍රවේශයට ඉඩ දෙන ආකාරය දැන ගන්න&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"තව දැන ගන්න"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"හරි"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"අවසර ඉල්ලීම වළක්වා ඇත"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"මෙම යෙදුම අතිරේක අවසර ඉල්ලා සිටින නමුත්, ප්‍රවාහ කිරීමේ සැසියක අවසර ලබා දිය නොහැක. පළමුව ඔබේ දුරකථනයෙන් අවසරය ලබා දෙන්න."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-sk-v34/strings.xml b/PermissionController/res/values-sk-v34/strings.xml
index 5f84f5059..e7212c8d5 100644
--- a/PermissionController/res/values-sk-v34/strings.xml
+++ b/PermissionController/res/values-sk-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Správa prístupu aplikácie k údajom o zdraví"</string>
<string name="location_settings" msgid="8863940440881290182">"Prístup k polohe"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"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="6846532794702613851">"Pre aplikácie a služby"</string>
</resources>
diff --git a/PermissionController/res/values-sk-watch/strings.xml b/PermissionController/res/values-sk-watch/strings.xml
index 8667362bb..1a7e89701 100644
--- a/PermissionController/res/values-sk-watch/strings.xml
+++ b/PermissionController/res/values-sk-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nedá sa zmeniť"</string>
<string name="generic_yes" msgid="2489207724988649846">"Áno"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Zrušiť"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Po celý čas"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Pri používaní"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Po celý čas"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Pri používaní"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Po celý čas"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Pri používaní"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Po celý čas"</string>
</resources>
diff --git a/PermissionController/res/values-sk/strings.xml b/PermissionController/res/values-sk/strings.xml
index ae8ea524c..403f66993 100644
--- a/PermissionController/res/values-sk/strings.xml
+++ b/PermissionController/res/values-sk/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"povolenia"</string>
<string name="cancel" msgid="8943320028373963831">"Zrušiť"</string>
<string name="back" msgid="6249950659061523680">"Späť"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Zavrieť"</string>
<string name="available" msgid="6007778121920339498">"Dostupné"</string>
<string name="blocked" msgid="9195547604866033708">"Blokované"</string>
<string name="on" msgid="280241003226755921">"Zapnuté"</string>
@@ -34,6 +35,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 +62,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 +87,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Všetky povolenia"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ďalšie možnosti aplikácie"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Žiadosť o povolenie"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear nepodporuje akciu inštalácie/odinštalovania."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Vyberte, k čomu môže pristupovať aplikácia &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikácia &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; bola aktualizovaná. Vyberte, k čomu bude mať prístup."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Zrušiť"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Vždy povoliť všetko"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Vždy sa opýtať"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Nepovoliť"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Povoliť obmedzený prístup"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Presná poloha"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Približná poloha"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Používať presnú polohu"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Keď je presná poloha vypnutá, aplikácie majú prístup k vašej približnej polohe"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g>: povolenie"</string>
<string name="app_permission_header" msgid="2951363137032603806">"<xliff:g id="PERM">%1$s</xliff:g>: prístup tejto aplikácie"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Prístup k položke <xliff:g id="PERM">%1$s</xliff:g> pre túto aplikáciu v zariadení <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Zobraziť všetky povolenia aplikácie <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Zobraziť všetky aplikácie s týmto povolením"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Zobraziť používanie mikrofónu Asistentom"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Odstrániť povolenia, ak sa aplikácia nepoužíva"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Odstraňovať povol. a uvoľňovať priestor"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pozastaviť aktivitu v nepoužívaných apl."</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Spravovať aplikáciu, ak sa nepoužíva"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Odstrániť povolenia, vymazať dočasné súbory a zastaviť upozornenia"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Odstrániť povolenia, vymazať dočasné súbory, zastaviť upozornenia a archivovať aplikáciu"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Ak túto aplikáciu niekoľko mesiacov nepoužijete, v záujme ochrany vašich údajov budú odstránené jej povolenia."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ak túto aplikáciu niekoľko mesiacov nepoužijete, v záujme ochrany vašich údajov budú odstránené tieto povolenia: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"V záujme ochrany vašich údajov boli odobrané povolenia aplikáciám, ktoré ste niekoľko mesiacov nepoužívali."</string>
@@ -219,7 +224,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"S povolením spravovať všetky súbory"</string>
<string name="ask_header" msgid="2633816846459944376">"Vždy sa opýtať"</string>
<string name="denied_header" msgid="903209608358177654">"Nepovolené"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> v zariadení <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Zobraziť ďalšie aplikácie s prístupom k všetkým súborom"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 deň}few{# dni}many{# dňa}other{# dní}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# hodina}few{# hodiny}many{# hodiny}other{# hodín}}"</string>
@@ -362,8 +368,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikácia na poznámky"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikácie, ktoré vám v zariadení umožňujú písať poznámky"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"poznámky"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Predvolená aplik. s peňaženkou"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikácia s peňaženkou"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikácie s peňaženkou môžu uložiť vaše kreditné alebo vernostné karty, kľúče od auta a iné položky, aby vám mohli pomáhať s rôznymi druhmi transakcií."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Chcete nastaviť <xliff:g id="APP_NAME">%1$s</xliff:g> ako predvolenú aplikáciu s peňaženkou?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nie sú potrebné žiadne povolenia"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Aktuálne predvolená"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Nabudúce sa nepýtať"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Nastav. ako predvol."</string>
@@ -426,8 +437,9 @@
<string name="default_apps_more" msgid="4078194675848858093">"Ďalšie predvolené nastavenia"</string>
<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_apps_for_private_profile" msgid="2022024112144880785">"Predvolené pre súkromný priestor"</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>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Zobrazovať detekciu spustenia asistenta"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Zobrazovať v stavovom riadku ikonu, keď bude pomocou mikrofónu aktivovaný hlasový asistent"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"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 médiám v zariadení?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"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 médiám v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup ku kontaktom?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k svojim kontaktom v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k polohe tohto zariadenia?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k polohe zariadenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikácia bude mať prístup k polohe iba vtedy, keď ju budete používať"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k polohe tohto zariadenia?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k polohe zariadenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Táto aplikácia môže požadovať nepretržitý prístup k vašej polohe, aj keď ju nepoužívate. "<annotation id="link">"Povolíte ho v nastaveniach."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Chcete zmeniť prístup k polohe pre aplikáciu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Chcete zmeniť prístup k polohe pre &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Táto aplikácia požaduje nepretržitý prístup k vašej polohe, aj keď ju nepoužívate. "<annotation id="link">"Povolíte ho v nastaveniach."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Chcete aplikácii <xliff:g id="APP_NAME">%1$s</xliff:g> povoliť vyhľadávať zariadenia v okolí, pripájať sa k nim a určovať ich relatívnu polohu?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; vyhľadávať zariadenia v okolí, určovať ich relatívnu polohu a pripájať sa k nim v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Chcete aplikácii <xliff:g id="APP_NAME">%1$s</xliff:g> povoliť vyhľadávať zariadenia v okolí, pripájať sa k nim a určovať ich vzájomnú polohu? "<annotation id="link">"Urobte to v nastaveniach."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Chcete zmeniť prístup k polohe aplikácie <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> z približnej polohy na presnú?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Chcete zmeniť prístup k polohe pre aplikáciu <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; z približnej na presnú?"</string>
<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_device_aware_coarselocation" msgid="8367540370912066757">"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 zariadenia &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_calendar" msgid="7161929851377463612">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k svojmu kalendáru v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_sms" msgid="6639977653040502291">"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 v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_storage" msgid="6933251810928606636">"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í &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_read_media_aural" msgid="7927884506238101064">"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 zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_read_media_visual" msgid="3122576538319059333">"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 zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"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 zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávať zvuk?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávať zvuk v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Táto aplikácia bude môcť nahrávať zvuk iba vtedy, keď ju budete používať"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávať zvuk?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; nahrávať zvuk v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Chcete zmeniť prístup k mikrofónu pre &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_activityRecognition" msgid="1243869530588745374">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k svojej fyzickej aktivite v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotiť a nahrávať videá v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotiť a nahrávať videá v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Chcete zmeniť prístup ku kamere pre &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_calllog" msgid="8220927190376843309">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k svojmu zoznamu telefonických hovorov v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"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 v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k dátam senzorov o vašich životných funkciách v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; používať dáta senzorov o vašich životných funkciách?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k dátam senzorov o vašich životných funkciách v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Ak chcete tejto aplikácii povoliť nepretržitý prístup k údajom telového senzora, a to aj v prípade, že ju nepoužívate, "<annotation id="link">"prejdite do nastavení."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k dátam telových senzorov počas jej používania?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Chcete ponechať aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; prístup k dátam telových senzorov v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; počas jej používania?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; posielať vám upozornenia?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Chcete povoliť aplikácii &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; posielať vám upozornenia v zariadení &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Ovládané povolenia"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> má prístup k polohe"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Vaša organizácia povoľuje aplikácii <xliff:g id="APP_NAME">%1$s</xliff:g> prístup k vašej polohe"</string>
@@ -505,13 +544,14 @@
<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>
<string name="privdash_label_none" msgid="5991866260360484858">"Žiadne"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Posledných\n24 hodín"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Posledných\n7 dní"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> percent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je chránená Androidom. Použitie povolení pre túto aplikáciu sa nezobrazuje v stavovom riadku ani na paneli ochrany súkromia, pretože vaše údaje sa spracúvajú v tomto zariadení."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je chránená Androidom. Použitie povolení tejto aplikácie sa nezobrazuje na paneli ochrany súkromia, pretože vaše údaje sa spracúvajú v tomto zariadení."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera zariadenia je blokovaná"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"V prípade aplikácií a služieb"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Keď zavoláte na tiesňovú linku, môžu sa stále zdieľať údaje mikrofónu."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Zmeniť"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Prístup ku kamere je vypnutý"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Prístup k mikrofónu je vypnutý"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Prístup k polohe je vypnutý"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Pre aplikácie pre palubný systém"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Pre požadované aplikácie"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Vyžaduje sa táto aplikácia"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Túto aplikáciu vyžaduje výrobca vášho auta"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Zabezpečenie, ochrana súkromia"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skontrolovať zariadenie"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Zavrieť"</string>
@@ -581,9 +628,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 +655,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Aktualizácie zdieľania údajov"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Niektoré aplikácie zmenili spôsob zdieľania údajov o polohe"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Nastavenia"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Otvorené <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Otvorené včera <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Otvorené <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Jednorazové heslo je 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Obmedzené nastavenie"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Z bezpečnostných dôvodov nie je toto nastavenie momentálne k dispozícii."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikácii bol prístup k povoleniu <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> zamietnutý"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Táto aplikácia vyžaduje prístup k citlivému povoleniu, čo môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že bez tohto obmedzeného povolenia nebude aplikácia správne fungovať. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ako povoliť prístup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikácii bol zamietnutý prístup a predvolene nemôže byť <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Táto aplikácia vyžaduje prístup k citlivým povoleniam, čo môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že bez týchto obmedzených povolení nebude aplikácia správne fungovať. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ako povoliť prístup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikácii bol zamietnutý prístup"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Prístup k tomuto povoleniu môže ohroziť vaše osobné údaje a finančné informácie.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Je možné, že bez tohto obmedzeného povolenia nebude aplikácia správne fungovať. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Ako povoliť prístup&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Ďalšie informácie"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Žiadosť o povolenie bola zablokovaná"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Táto aplikácia požaduje ďalšie povolenia, ale povolenia nie je možné udeliť v relácii streamovania. Najprv udeľte povolenie v telefóne."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Na účely tiesňového volania alebo textovej správy"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Poloha bola odoslaná tiesňovej linke"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Táto aplikácia získala počas hovoru s tiesňovou linkou alebo odosielania správy na ňu prístup k polohe vášho zariadenia. Môže sa to stať, aj keď daná aplikácia nemá povolený prístup k polohe alebo keď je poloha zariadenia vypnutá. "<a href="https://support.google.com/android/answer/9319337">"Ďalšie informácie"</a></string>
</resources>
diff --git a/PermissionController/res/values-sl-v34/strings.xml b/PermissionController/res/values-sl-v34/strings.xml
index 18afff3d1..11fafdf93 100644
--- a/PermissionController/res/values-sl-v34/strings.xml
+++ b/PermissionController/res/values-sl-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Upravljajte dostop aplikacij do podatkov o zdravju."</string>
<string name="location_settings" msgid="8863940440881290182">"Dostop do lokacije"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Za aplikacije in storitve. Če je ta nastavitev izklopljena, bodo podatki mikrofona morda še vedno deljeni, ko pokličete številko za klic v sili."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Za aplikacije in storitve."</string>
</resources>
diff --git a/PermissionController/res/values-sl-watch/strings.xml b/PermissionController/res/values-sl-watch/strings.xml
index 83cff82ee..f93ba26b5 100644
--- a/PermissionController/res/values-sl-watch/strings.xml
+++ b/PermissionController/res/values-sl-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Ni mogoče sprem."</string>
<string name="generic_yes" msgid="2489207724988649846">"Da"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Prekliči"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Ves čas"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Med uporabo aplikacije"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Ves čas"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Med uporabo aplikacije"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Ves čas"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Med uporabo aplikacije"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Ves čas"</string>
</resources>
diff --git a/PermissionController/res/values-sl/strings.xml b/PermissionController/res/values-sl/strings.xml
index 5265a0724..f8beb58e2 100644
--- a/PermissionController/res/values-sl/strings.xml
+++ b/PermissionController/res/values-sl/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"dovoljenja"</string>
<string name="cancel" msgid="8943320028373963831">"Prekliči"</string>
<string name="back" msgid="6249950659061523680">"Nazaj"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Zapri"</string>
<string name="available" msgid="6007778121920339498">"Na voljo"</string>
<string name="blocked" msgid="9195547604866033708">"Blokirano"</string>
<string name="on" msgid="280241003226755921">"Vklopljeno"</string>
@@ -34,6 +35,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 +62,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 +87,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Vsa dovoljenja"</string>
<string name="other_permissions" msgid="2901186127193849594">"Druge zmožnosti aplikacije"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Zahteva za dovoljenje"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Dejanja namestitve in odstranitve v sistemu Android Wear niso podprta."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Izberite, do česa aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovolite dostop"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; je posodobljena. Izberite, do česa tej aplikaciji dovolite dostop."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Prekliči"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Vedno dovoli vse"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Vedno vprašaj"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ne dovoli"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Dovoli omejen dostop"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Natančna lokacija"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Približna lokacija"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Uporabi natančno lokacijo"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Ko je zaznavanje natančne lokacije izklopljeno, lahko aplikacije dostopajo do vaše približne lokacije."</string>
<string name="app_permission_title" msgid="2090897901051370711">"Dovoljenje »<xliff:g id="PERM">%1$s</xliff:g>«"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Dostop te aplikacije do dovoljenja »<xliff:g id="PERM">%1$s</xliff:g>«"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Dostop do dovoljenja »<xliff:g id="PERM">%1$s</xliff:g>« za to aplikacijo v napravi <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Ogled vseh dovoljenj za aplikacijo <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Ogled vseh aplikacij s tem dovoljenjem"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Prikaz uporabe pomožnega mikrofona"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Odstrani dovoljenja, če aplikacija ni v uporabi"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Odstrani dovoljenja in sprosti prostor"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Zaustavi dejavnost aplikacije ob neuporabi"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Upravljanje aplikacije ob neuporabi"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Dovoljenja se odstranijo, začasne datoteke se izbrišejo in prikazovanje obvestil se ustavi."</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Odstranitev dovoljenj, izbris začasnih datotek, ustavitev prikazovanja obvestil in arhiviranje aplikacije"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Z namenom zaščite podatkov bodo dovoljenja za to aplikacijo odstranjena, če je več mesecev ne boste uporabljali."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Če aplikacije več mesecev ne boste uporabljali, bodo z namenom zaščite podatkov odstranjena ta dovoljenja: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Z namenom zaščite podatkov so bila odstranjena dovoljenja za aplikacije, ki jih več mesecev niste uporabljali."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Dovoljeno je upravljanje vseh datotek"</string>
<string name="ask_header" msgid="2633816846459944376">"Vedno vprašaj"</string>
<string name="denied_header" msgid="903209608358177654">"Ni dovoljeno"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"Skupina dovoljenj »<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>« v napravi <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Prikaži več aplikacij z dostopom do vseh datotek"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dan}one{# dan}two{# dneva}few{# dni}other{# dni}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ura}one{# ura}two{# uri}few{# ure}other{# ur}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikacija za zapiske"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikacije, ki vam omogočajo ustvarjanje zapiskov v napravi."</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"zapiski"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Privzeta aplikacija denarnice"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikacija denarnice"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"V aplikacijah denarnice lahko shranite kreditne kartice in kartice zvestobe, avtomobilske ključe ter druge stvari, ki vam omogočajo različne oblike transakcij."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Želite nastaviti <xliff:g id="APP_NAME">%1$s</xliff:g> kot privzeto aplikacijo denarnice?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nobeno dovoljenje ni potrebno"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Trenutna privzeta nastavitev"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ne vprašaj me več"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Nastavi kot privzeto"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Več privzetih aplikacij"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Odpiranje povezav"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Privzeto za delo"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Privzeto za zasebni prostor"</string>
<string name="default_app_none" msgid="9084592086808194457">"Brez"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(privzeta v sistemu)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Ni aplikacij"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Prikaz zaznavanja sprožilca za pomočnika"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Prikaz ikone v vrstici stanja, ko z mikrofonom aktivirate glasovnega pomočnika"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do fotografij in predstavnosti v napravi?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do fotografij in predstavnosti v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do stikov?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do vaših stikov v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do lokacije te naprave?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do lokacije naprave &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacija bo imela dostop do lokacije samo, ko aplikacijo uporabljate"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do lokacije te naprave?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do lokacije naprave &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ta aplikacija bo morda želela imeti stalen dostop do vaše lokacije, tudi ko aplikacije ne boste uporabljali. "<annotation id="link">"Omogočite v nastavitvah."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Želite spremeniti dostop do lokacije za aplikacijo &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Želite spremeniti dostop do lokacije za aplikacijo &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ta aplikacija želi imeti stalen dostop do vaše lokacije, tudi ko aplikacije ne uporabljate. "<annotation id="link">"Omogočite v nastavitvah."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Ali apl. &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovolite iskanje naprav v bližini, povezovanje z njimi in določanje njihove rel. lokacije?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti iskanje naprav v bližini, povezavo z njimi in določanje njihovega relativnega položaja v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Ali aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovolite iskanje naprav v bližini, povezovanje z njimi in določanje njihove rel. lokacije? "<annotation id="link">"Omogočite v nastavitvah."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Ali želite aplikaciji <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> omogočiti dostop do natančne lokacije namesto približne?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Želite dostop do lokacije za aplikacijo <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; spremeniti iz približnega v natančnega?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do približne lokacije te naprave?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do približne lokacije naprave &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Natančna"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Približna"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do koledarja?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do vašega koledarja v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pošiljanje in ogled sporočil SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti pošiljanje in ogled sporočil SMS v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do fotografij, predstavnosti in datotek v svoji napravi?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do fotografij, predstavnosti in datotek v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do &lt;b&gt;fotografij, videoposnetkov, glasbe in zvočnih datotek&lt;/b&gt; v tej napravi?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do &lt;b&gt;fotografij, videoposnetkov, glasbe, zvočnih datotek in drugih datotek&lt;/b&gt; v tej napravi?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do glasbe in zvočnih datotek v tej napravi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do glasbe in zvočnih datotek v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do fotografij in videoposnetkov v tej napravi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do fotografij in videoposnetkov v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do več fotografij in videoposnetkov v tej napravi?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do več fotografij in videoposnetkov v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; snemanje zvoka?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti snemanje zvoka v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacija bo lahko snemala zvok le med vašo uporabo aplikacije."</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti snemanje zvoka?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti snemanje zvoka v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ta aplikacija bo morda želela vseskozi snemati zvok, tudi ko aplikacije ne uporabljate. "<annotation id="link">"To omogočite v nastavitvah."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Želite spremeniti dostop do mikrofona za aplikacijo &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Želite spremeniti dostop do mikrofona za aplikacijo &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ta aplikacija želi vseskozi snemati zvok, tudi ko aplikacije ne uporabljate. "<annotation id="link">"To omogočite v nastavitvah."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dostop do svoje telesne dejavnosti?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do vaše telesne dejavnosti v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Dovolite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; fotografiranje in snemanje videoposnetkov?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti fotografiranje in snemanje videoposnetkov v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacija bo lahko snemala fotografije in videoposnetke le med vašo uporabo aplikacije."</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; omogočiti fotografiranje in snemanje videoposnetkov?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti fotografiranje in snemanje videoposnetkov v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ta aplikacija bo morda želela vseskozi snemati fotografije in videoposnetke, tudi ko aplikacije ne uporabljate. "<annotation id="link">"To omogočite v nastavitvah."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Želite spremeniti dostop do fotoaparata za aplikacijo &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Želite spremeniti dostop do fotoaparata za aplikacijo &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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_device_aware_calllog" msgid="8220927190376843309">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do dnevnikov telefonskih klicev v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti klicanje in upravljanje telefonskih klicev v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do podatkov tipal o vitalnih znakih v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti dostop do podatkov tipal o vitalnih znakih v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; še naprej dovoliti dostop do podatkov tipala 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; dovoliti, da vam pošilja obvestila?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Želite aplikaciji &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; dovoliti pošiljanje obvestil v napravi &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Brez"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"V zadnjih\n24 urah"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Zadnjih\n7 dni"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> %%"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaščitena z Androidom. Ker obdelava podatkov poteka v tej napravi, uporaba dovoljenj za to aplikacijo ni prikazana v vrstici stanja ali na nadzorni plošči za zasebnost."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaščitena z Androidom. Ker obdelava podatkov poteka v tej napravi, uporaba dovoljenj za to aplikacijo ni prikazana na nadzorni plošči za zasebnost."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Fotoaparat naprave je blokiran"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Za aplikacije in storitve"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Podatki mikrofona bodo morda še vedno deljeni, ko pokličete številko za klic v sili."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Spremeni"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Dostop do fotoaparata je izklopljen"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Dostop do mikrofona je izklopljen"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Dostop do lokacije je izklopljen"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Za informativno-razvedrilne aplikacije"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Za obvezne aplikacije"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ta aplikacija je obvezna"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"To aplikacijo zahteva proizvajalec avtomobila"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Varnost in zasebnost"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Preglej napravo"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Opusti"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Posodobitve deljenja podatkov"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Nekatere aplikacije so spremenile način deljenja vaših lokacijskih podatkov."</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Nastavitve"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Zadnji dostop: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Zadnji dostop včeraj: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Zadnji dostop: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Enkratno geslo je 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Omejena nastavitev"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Zaradi vaše varnosti ta nastavitev trenutno ni na voljo."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikaciji je bil zavrnjen dostop do dovoljenja »<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>«"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacija je zahtevala dostop do občutljivega dovoljenja, ki lahko ogrozi vaše osebne in finančne podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija morda ne bo pravilno delovala brez tega omejenega dovoljenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Preberite, kako omogočite dostop&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikaciji je bil zavrnjen dostop do privzete vloge »<xliff:g id="ROLE_NAME">%1$s</xliff:g>«"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacija je zahtevala dostop do občutljivih dovoljenj, ki lahko ogrozijo vaše osebne in finančne podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija morda ne bo pravilno delovala brez teh omejenih dovoljenj. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Preberite, kako omogočite dostop&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikaciji je bil zavrnjen dostop"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Dostop do tega dovoljenja lahko ogrozi vaše osebne in finančne podatke.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Aplikacija morda ne bo pravilno delovala brez tega omejenega dovoljenja. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Preberite, kako omogočite dostop&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Več o tem"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"V redu"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Zahteva za dovoljenje je bila prezrta"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ta aplikacija zahteva dodatna dovoljenja, vendar teh ni mogoče odobriti med sejo pretočnega predvajanja. Dovoljenje najprej odobrite v telefonu."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Za klic ali sporočilo v sili"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Lokacija je poslana reševalnim službam"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ta aplikacija je dostopala do lokacije naprave med klicanjem ali pošiljanjem sporočila na številko za klic v sili. To se lahko zgodi tudi, če aplikacija nima dovoljenja za lokacijo ali če je lokacija naprave izklopljena. "<a href="https://support.google.com/android/answer/9319337">"Več o tem"</a></string>
</resources>
diff --git a/PermissionController/res/values-sq-v34/strings.xml b/PermissionController/res/values-sq-v34/strings.xml
index bf9c922cd..960da8d7d 100644
--- a/PermissionController/res/values-sq-v34/strings.xml
+++ b/PermissionController/res/values-sq-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Menaxho qasjen e aplikacioneve te të dhënat e shëndetit"</string>
<string name="location_settings" msgid="8863940440881290182">"Qasja te vendndodhja"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Për aplikacionet dhe shërbimet. Nëse ky cilësim është joaktiv, të dhënat e mikrofonit mund të vazhdojnë të ndahen kur telefonon një numër urgjence."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Për aplikacionet dhe shërbimet"</string>
</resources>
diff --git a/PermissionController/res/values-sq-watch/strings.xml b/PermissionController/res/values-sq-watch/strings.xml
index 3043bf520..cddff97c5 100644
--- a/PermissionController/res/values-sq-watch/strings.xml
+++ b/PermissionController/res/values-sq-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Nuk mund të ndryshohet"</string>
<string name="generic_yes" msgid="2489207724988649846">"Po"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Anulo"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Gjatë gjithë kohës"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Kur përdor aplikacionin"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Gjatë gjithë kohës"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Kur përdor aplikacionin"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Gjatë gjithë kohës"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Kur përdor aplikacionin"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Gjatë gjithë kohës"</string>
</resources>
diff --git a/PermissionController/res/values-sq/strings.xml b/PermissionController/res/values-sq/strings.xml
index bfda0b905..7a8b54843 100644
--- a/PermissionController/res/values-sq/strings.xml
+++ b/PermissionController/res/values-sq/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"lejet"</string>
<string name="cancel" msgid="8943320028373963831">"Anulo"</string>
<string name="back" msgid="6249950659061523680">"Pas"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"Ofrohet"</string>
<string name="blocked" msgid="9195547604866033708">"Bllokuar"</string>
<string name="on" msgid="280241003226755921">"Aktiv"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Të gjitha lejet"</string>
<string name="other_permissions" msgid="2901186127193849594">"Kapacitete të tjera të aplikacionit"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Kërkesa e lejes"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Instalo/çinstalo veprimet që nuk mbështeten në Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Zgjidh se ku do të lejohet të ketë qasje &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; është përditësuar. Zgjidh se ku do të lejohet të ketë qasje ky aplikacion."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Anulo"</string>
@@ -191,12 +193,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Lejoji gjithmonë të gjitha"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Pyet çdo herë"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Mos lejo"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Lejo qasjen e kufizuar"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Vendndodhja e saktë"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Vendndodhja e përafërt"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Përdor vendndodhjen e saktë"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kur është joaktive vendndodhja e saktë, aplikacionet mund të qasen te vendndodhja jote e përafërt"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Leje për te \"<xliff:g id="PERM">%1$s</xliff:g>\""</string>
<string name="app_permission_header" msgid="2951363137032603806">"Qasja te \"<xliff:g id="PERM">%1$s</xliff:g>\" për këtë aplikacion"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Qasja te <xliff:g id="PERM">%1$s</xliff:g> për këtë aplikacion në <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Shiko të gjitha lejet e aplikacionit \"<xliff:g id="APP">%1$s</xliff:g>\""</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Shiko të gjitha aplikacionet me këtë leje"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Shfaq përdorimin e mikrofonit të \"Asistentit\""</string>
@@ -204,7 +208,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Hiq lejet nëse aplikacioni nuk është përdorur"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Hiq lejet dhe liro hapësirën"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Ndërprit aktivitetin nëse nuk përdoret"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Menaxho aplikacionin nëse nuk përdoret"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Hiq lejet, fshi skedarët e përkohshëm dhe ndalo njoftimet"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Hiq lejet, fshi skedarët e përkohshëm, ndalo njoftimet dhe arkivo aplikacionin"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Për të mbrojtur të dhënat e tua, lejet për këtë aplikacion do të hiqen nëse aplikacioni nuk përdoret për disa muaj."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Për të mbrojtur të dhënat e tua, nëse aplikacioni nuk është përdorur për disa muaj, lejet e mëposhtme do të hiqen: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Për të mbrojtur të dhënat e tua, lejet janë hequr nga aplikacionet që nuk i ke përdorur për disa muaj."</string>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Lejohet të menaxhojë të gjithë skedarët"</string>
<string name="ask_header" msgid="2633816846459944376">"Pyet çdo herë"</string>
<string name="denied_header" msgid="903209608358177654">"Nuk lejohet"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> në <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Shiko më shumë aplikacione me qasje tek të gjithë skedarët"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ditë}other{# ditë}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# orë}other{# orë}}"</string>
@@ -384,7 +391,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>
@@ -401,6 +408,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Aplikacioni për shënime"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Aplikacione që të lejojnë të mbash shënime në pajisjen tënde"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"shënime"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Aplikacioni i parazgjedhur i portofolit"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Aplikacioni i portofolit"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Aplikacionet e portofolit mund të ruajnë kartat e tua të kreditit dhe të besnikërisë, çelësat e makinës dhe gjëra të tjera për të të ndihmuar për forma të ndryshme transaksionesh."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Të caktohet <xliff:g id="APP_NAME">%1$s</xliff:g> si aplikacioni yt i parazgjedhur i portofolit?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Nuk nevojitet asnjë leje"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Parazgjedhja aktuale"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Mos pyet më"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Cakto si parazgjedhje"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Parazgjedhje të tjera"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Hapja e lidhjeve"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Të parazgjedhura për punën"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Të parazgjedhurat për hapësirën private"</string>
<string name="default_app_none" msgid="9084592086808194457">"Asnjë"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Parazgjedhja e sistemit)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Nuk ka aplikacione"</string>
@@ -455,48 +468,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Shfaq zbulimin e aktivizimit të asistentit"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Shfaq ikonën në shiritin e statusit kur përdoret mikrofoni për të aktivizuar asistentin me zë"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë dhe media në pajisjen tënde?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë dhe media në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te kontaktet e tua?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te kontaktet e tua në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e kësaj pajisjeje?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Aplikacioni do të ketë qasje te vendndodhja vetëm kur po e përdor aplikacionin"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e kësaj pajisjeje?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ky aplikacion mund të dëshirojë të ketë qasje në vendndodhjen tënde gjatë gjithë kohës, edhe kur nuk po e përdor aplikacionin. "<annotation id="link">"Lejoje te cilësimet"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Do ta ndryshosh qasjen për vendndodhjen për &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Të ndryshohet qasja te vendndodhja për &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ky aplikacion dëshiron të ketë qasje në vendndodhjen tënde gjatë gjithë kohës, edhe kur nuk po e përdor aplikacionin. "<annotation id="link">"Lejoje te cilësimet"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Të lejohet &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të gjejë, të lidhet dhe të përcaktojë pozicionin relativ të pajisjeve në afërsi?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të gjejë, të lidhet dhe të përcaktojë pozicionin e përafërt të pajisjeve në afërsi në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Të lejohet &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të gjejë, të lidhet dhe të përcaktojë pozicionin relativ të pajisjeve në afërsi? "<annotation id="link">"Lejoje te cilësimet."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Të ndryshohet qasja që ka <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> te vendndodhja nga \"e përafërt\" në \"e saktë\"?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Të ndryshohet qasja e <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> te vendndodhja në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; nga e përafërt në e saktë?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e përafërt e kësaj pajisjeje?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te vendndodhja e përafërt e &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"E saktë"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"E përafërt"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te kalendari yt?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te kalendari yt në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të dërgojë dhe të shikojë mesazhet SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të dërgojë dhe të shikojë mesazhet SMS në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë, media dhe skedarët në pajisjen tënde?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë, media dhe skedarët në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te &lt;b&gt;fotografitë, videot, muzika dhe audioja&lt;/b&gt; në këtë pajisje?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Të lejohet që &lt;b&amp;gt<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te &lt;b&gt;fotografitë, videot, muzika, audioja e të tjera&lt;/b&gt; në pajisje?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te muzika dhe te audioja në këtë pajisje?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te muzika dhe audioja në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë dhe videot në këtë pajisje?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te fotografitë dhe videot në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të qaset te më shumë fotografi dhe video në pajisje?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje në më shumë fotografi dhe video në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Aplikacioni do të mund të regjistrojë audion vetëm kur ti po e përdor aplikacionin"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të regjistrojë audio në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Ky aplikacion mund të dëshirojë të regjistrojë audion gjatë gjithë kohës, edhe kur ti nuk po e përdor aplikacionin. "<annotation id="link">"Lejoje te cilësimet"</annotation>"."</string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Të ndryshohet qasja për mikrofonin për &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Të ndryshohet qasja te mikrofoni për &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Ky aplikacion dëshiron të regjistrojë audion gjatë gjithë kohës, edhe kur ti nuk po e përdor aplikacionin. "<annotation id="link">"Lejoje te cilësimet"</annotation>"."</string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje në aktivitetin tënd fizik?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje tek aktiviteti yt fizik në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të nxjerrë fotografi dhe të regjistrojë video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të nxjerrë fotografi dhe të regjistrojë video në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Aplikacioni do të mund të nxjerrë fotografi dhe të regjistrojë video vetëm kur ti po e përdor aplikacionin"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të nxjerrë fotografi dhe të regjistrojë video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të nxjerrë fotografi dhe të regjistrojë video në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ky aplikacion mund të dëshirojë të nxjerrë fotografi dhe video gjatë gjithë kohës, edhe kur ti nuk po e përdor aplikacionin. "<annotation id="link">"Lejoje te cilësimet"</annotation>"."</string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Të ndryshohet qasja për kamerën për &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Të ndryshohet qasja te kamera për &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ky aplikacion dëshiron të nxjerrë fotografi dhe video gjatë gjithë kohës, edhe kur ti nuk po e përdor aplikacionin. "<annotation id="link">"Lejoje te cilësimet"</annotation>"."</string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Të lejohet &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje në evidencat e tua të telefonatave?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje tek evidencat e telefonatave të telefonit në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të kryejë dhe të menaxhojë telefonata?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të bëjë dhe të menaxhojë telefonatat në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te të dhënat e sensorëve rreth shenjave të tua jetësore?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te të dhënat e sensorëve rreth shenjave jetësore në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Aplikacioni kërkon qasje te të dhënat e sensorëve rreth shenjave të tua jetësore gjithmonë, edhe kur nuk e përdor aplikacionin. Për ta bërë këtë ndryshim, "<annotation id="link">"shko te cilësimet."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te të dhënat e sensorëve rreth shenjave të tua jetësore?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te të dhënat e sensorëve rreth shenjave jetësore në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Për të lejuar këtë aplikacion që të ketë qasje te të dhënat e sensorit të trupit gjithmonë, edhe kur nuk e përdor aplikacionin, "<annotation id="link">"shko te cilësimet."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Të lejohet akoma që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te të dhënat e sensorit të trupit kur aplikacioni përdoret?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Të lejohet akoma që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të ketë qasje te të dhënat e sensorëve të trupit në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; kur aplikacioni është në përdorim?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të të dërgojë njoftime?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Të lejohet që &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; të të dërgojë njoftime në &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Lejet e kontrolluara"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> ka qasje te vendndodhja"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Organizata jote lejon që <xliff:g id="APP_NAME">%1$s</xliff:g> të ketë qasje te vendndodhja jote"</string>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Asnjë"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Në 24 orët\ne kaluara"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 ditët\ne fundit"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>: <xliff:g id="PERCENT">%2$d</xliff:g> përqind"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> mbrohet nga Android. Duke qenë se të dhënat e tua përpunohen në këtë pajisje, përdorimi i lejes së aplikacionit nuk shfaqet në shiritin e statusit ose në panelin tënd të privatësisë."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> mbrohet nga Android. Duke qenë se të dhënat e tua përpunohen në këtë pajisje, përdorimi i lejes së aplikacionit nuk shfaqet në panelin tënd të privatësisë."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera e pajisjes është bllokuar"</string>
@@ -520,6 +561,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Për aplikacionet dhe shërbimet"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Të dhënat e mikrofonit mund të vazhdojnë të ndahen kur telefonon një numër urgjence."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Ndrysho"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Qasja te kamera është çaktivizuar"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Qasja te mikrofoni është joaktive"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Qasja te vendndodhja është joaktive"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Për aplikacionet info-argëtuese"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Për aplikacionet e domosdoshme"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ky aplikacion është i domosdoshëm"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ky aplikacion kërkohet nga prodhuesi i makinës sate"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Siguria dhe privatësia"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skano pajisjen"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Hiq"</string>
@@ -619,4 +667,26 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Përditësimet për ndarjen e të dhënave"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Disa aplikacione ndryshuan mënyrën se si mund të ndajnë të dhënat e vendndodhjes sate"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Cilësimet"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Qasja e fundit në <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Qasja e fundit dje në <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Qasja e fundit më <xliff:g id="TIME_DATE_0">%1$s</xliff:g> në <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Fjalëkalimi yt njëpërdorimësh është 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Cilësim i kufizuar"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Për sigurinë tënde, ky cilësim nuk ofrohet për momentin."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Aplikacionit iu refuzua qasja te: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Aplikacioni kërkoi qasje në një leje delikate që mund t\'i vendosë në rrezik informacionet e tua personale dhe financiare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Ka mundësi që aplikacioni të mos funksionojë si duhet pa këtë leje të kufizuar. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Mëso se si të lejosh qasjen&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Aplikacionit iu refuzua qasja për të qenë parazgjedhja për: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Aplikacioni kërkoi qasje në leje delikate që mund t\'i vendosin në rrezik informacionet e tua personale dhe financiare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Ka mundësi që aplikacioni të mos funksionojë si duhet pa këto leje të kufizuara. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Mëso se si të lejosh qasjen&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Aplikacionit iu refuzua qasja"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Qasja në këtë leje mund t\'i vendosë në rrezik informacionet e tua personale dhe financiare.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Ka mundësi që aplikacioni të mos funksionojë si duhet pa këtë leje të kufizuar. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Mëso se si të lejosh qasjen&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Mëso më shumë"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Në rregull"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Kërkesa për leje është ndaluar"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ky aplikacion po kërkon leje shtesë, por lejet nuk mund të jepen në një seancë transmetimi. Fillimisht jep lejen në telefonin tënd."</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-sr-v34/strings.xml b/PermissionController/res/values-sr-v34/strings.xml
index 89233996c..3708c5d93 100644
--- a/PermissionController/res/values-sr-v34/strings.xml
+++ b/PermissionController/res/values-sr-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-sr-watch/strings.xml b/PermissionController/res/values-sr-watch/strings.xml
index 6d9cff946..6f21adc03 100644
--- a/PermissionController/res/values-sr-watch/strings.xml
+++ b/PermissionController/res/values-sr-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Не може да се промени"</string>
<string name="generic_yes" msgid="2489207724988649846">"Да"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Откажи"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Све време"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"При коришћењу апликације"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Све време"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"При коришћењу апликације"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Све време"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"При коришћењу апликације"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Све време"</string>
</resources>
diff --git a/PermissionController/res/values-sr/strings.xml b/PermissionController/res/values-sr/strings.xml
index 40a58b981..973ccad59 100644
--- a/PermissionController/res/values-sr/strings.xml
+++ b/PermissionController/res/values-sr/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"дозволе"</string>
<string name="cancel" msgid="8943320028373963831">"Откажи"</string>
<string name="back" msgid="6249950659061523680">"Назад"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Затвори"</string>
<string name="available" msgid="6007778121920339498">"Доступно"</string>
<string name="blocked" msgid="9195547604866033708">"Блокирано"</string>
<string name="on" msgid="280241003226755921">"Укључено"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Радње Инсталирај/Деинсталирај нису подржане у Wear-у."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Изаберите чему &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; може да приступа"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; је ажурирана. Изаберите чему ова апликација може да приступа."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Откажи"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"Дозволи ограничен приступ"</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_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_with_device_name" msgid="7193042925656173271">"Ова апликација има приступ за: <xliff:g id="PERM">%1$s</xliff:g> на уређају <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Управљајте апликацијом ако се не користи"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Уклоните дозволе, избришите привремене фајлове и зауставите обавештења"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Уклоните дозволе, избришите привремене фајлове, зауставите обавештења и архивирајте апликацију"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Ради заштите података, дозволе за ову апликацију се уклањају ако се апликација не користи пар месеци."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ради заштите података, следеће дозволе се уклањају ако се апликација не користи пар месеци: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Ради заштите података, дозволе су уклоњене из апликација које нисте користили пар месеци."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Имају дозволу за управљање свим датотекама"</string>
<string name="ask_header" msgid="2633816846459944376">"Питај сваки пут"</string>
<string name="denied_header" msgid="903209608358177654">"Није дозвољено"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> на уређају <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Прикажи још апликација са приступом свим фајловима"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 дан}one{# дан}few{# дана}other{# дана}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# сат}one{# сат}few{# сата}other{# сати}}"</string>
@@ -373,7 +379,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 +391,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Подразумевана апликација новчаника"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Апликација Новчаник"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Апликације новчаника могу да чувају ваше кредитне картице и картице лојалности, кључеве од аутомобила и друге ствари како би вам помогли при различитим трансакцијама."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Желите да подесите <xliff:g id="APP_NAME">%1$s</xliff:g> као подразумевану апликацију новчаника?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Није потребна ниједна дозвола"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Тренутно подразумевана"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Не питај поново"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Подеси као подразум."</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Још подразумеваних апликација"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Отварање линкова"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Подразумевана за посао"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Подразумевано за приватан простор"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ништа"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Подразумевана системска)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Нема апликација"</string>
@@ -436,7 +448,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>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа сликама и медијима на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа контактима на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа локацији уређаја &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа локацији уређаја &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Мењате приступ локацији за апликацију &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; проналази уређаје у близини, повезује се са њима и утврђује њихову релативну позицију на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"Мењате приступ апликације <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> локацији уређаја &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа приближној локацији уређаја &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа календару на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; шаље и прегледа SMS поруке на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа сликама, медијима и фајловима на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_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_device_aware_read_media_aural" msgid="7927884506238101064">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа музици и звуку на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Дозвољавате да &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа сликама и видеима на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа и другим сликама и видеима на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима звук на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Мењате приступ микрофону за апликацију &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа подацима о физичким активностима на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; снима слике и видео на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Мењате приступ камери за апликацију &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа евиденцији телефонских позива на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; упућује телефонске позиве на уређају &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа подацима сензора о виталним функцијама на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; приступа подацима сензора о виталним функцијама на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Да бисте дозволили овој апликацији да све време приступа подацима сензора за тело, чак и када не користите апликацију, "<annotation id="link">"идите у подешавања."</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_device_aware_sensors" msgid="5542771499929819675">"Дозвољавате да апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; током коришћења и даље приступа подацима сензора за тело на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Дозвољавате да вам апликација &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; шаље обавештења на: &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Ништа"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Претходна\n24 сата"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"У претходних\n7 дана"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> посто"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Android штити <xliff:g id="APP_NAME">%1$s</xliff:g>. Пошто се подаци обрађују на овом уређају, коришћење дозвола за ову апликацију се не приказује на статусној траци или контролној табли за приватност."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Android штити <xliff:g id="APP_NAME">%1$s</xliff:g>. Пошто се подаци обрађују на овом уређају, коришћење дозвола за ову апликацију се не приказује на контролној табли за приватност."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Камера уређаја је блокирана"</string>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Приступ камери је искључен"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Приступ микрофону је искључен"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Приступ локацији је искључен"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"За апликације за информације и забаву"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"За обавезне апликације"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Ова апликација је обавезна"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Ову апликацију захтева произвођач аутомобила"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Приступано: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Приступано јуче: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Приступано: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Једнократна лозинка је 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Ограничено подешавање"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ово подешавање је тренутно недоступно ради ваше безбедности."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Апликацији није дозвољен приступ дозволи: <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Апликација је затражила приступ осетљивој дозволи, што може да угрози безбедност личних и финансијских података.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Апликација можда неће радити исправно без ове ограничене дозволе. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Сазнајте како да дозволите приступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Апликацији није дозвољен приступ да постане подразумевана: <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Апликација је затражила приступ осетљивим дозволама, што може да угрози безбедност личних и финансијских података.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Апликација можда неће радити исправно без ових ограничених дозвола. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Сазнајте како да дозволите приступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Апликацији није дозвољен приступ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Приступ овој дозволи може да угрози безбедност личних и финансијских података.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Апликација можда неће радити исправно без ове ограничене дозволе. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Сазнајте како да дозволите приступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Сазнајте више"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Важи"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Захтев за дозволу је блокиран"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ова апликација захтева додатне дозволе, али дозволе не могу да се дају у сесији стримовања. Прво дајте дозволу на телефону."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"За хитни позив или поруку"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Локација је послата хитним службама"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ова апликација је приступила локацији уређаја током позивања броја за хитне случајеве или слања поруке том броју. То може да се догоди чак и када апликација нема дозволу за локацију или је локација уређаја искључена. "<a href="https://support.google.com/android/answer/9319337">"Сазнајте више"</a></string>
</resources>
diff --git a/PermissionController/res/values-sv-v34/strings.xml b/PermissionController/res/values-sv-v34/strings.xml
index 09e204e2e..f6e4f3571 100644
--- a/PermissionController/res/values-sv-v34/strings.xml
+++ b/PermissionController/res/values-sv-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Hantera appars åtkomst till din hälsodata"</string>
<string name="location_settings" msgid="8863940440881290182">"Platsåtkomst"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"För appar och tjänster. Om inställningen är inaktiverad kan mikrofondata fortfarande delas när du ringer ett nödnummer"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"För appar och tjänster"</string>
</resources>
diff --git a/PermissionController/res/values-sv-watch/strings.xml b/PermissionController/res/values-sv-watch/strings.xml
index 0a170e27e..9086da508 100644
--- a/PermissionController/res/values-sv-watch/strings.xml
+++ b/PermissionController/res/values-sv-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Kan inte ändras"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ja"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Avbryt"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Hela tiden"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"När appen används"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Hela tiden"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"När appen används"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Hela tiden"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"När appen används"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Hela tiden"</string>
</resources>
diff --git a/PermissionController/res/values-sv/strings.xml b/PermissionController/res/values-sv/strings.xml
index 7d53baf1a..9ed421d42 100644
--- a/PermissionController/res/values-sv/strings.xml
+++ b/PermissionController/res/values-sv/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"behörigheter"</string>
<string name="cancel" msgid="8943320028373963831">"Avbryt"</string>
<string name="back" msgid="6249950659061523680">"Tillbaka"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Stäng"</string>
<string name="available" msgid="6007778121920339498">"Tillgänglig"</string>
<string name="blocked" msgid="9195547604866033708">"Blockerad"</string>
<string name="on" msgid="280241003226755921">"På"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Alla behörigheter"</string>
<string name="other_permissions" msgid="2901186127193849594">"Andra appbehörigheter"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Behörighetsbegäran"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Åtgärder för att installera/avinstallera stöds inte på Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Välj vad du vill ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; har uppdaterats. Välj vad du vill ge appen åtkomst till."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Avbryt"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Tillåt alltid alla"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Fråga varje gång"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Tillåt inte"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Tillåt begränsad åtkomst"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Exakt plats"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Ungefärlig plats"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Använd exakt plats"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"När exakt plats har inaktiverats har appar åtkomst till din ungefärliga plats"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Behörighet till <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Åtkomst till <xliff:g id="PERM">%1$s</xliff:g> för appen"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Åtkomst till <xliff:g id="PERM">%1$s</xliff:g> för den här appen på <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Visa alla behörigheter för <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Visa alla appar med den här behörigheten"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Visa mikrofonanvändning för assistenten"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Ta bort behörigheter om en app inte används"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Ta bort behörigheter och frigör utrymme"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Pausa appaktivitet om appen inte används"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Hantera appen om den inte används"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Ta bort behörigheter, radera tillfälliga filer och hindra aviseringar"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Ta bort behörigheter, radera tillfälliga filer, hindra aviseringar och arkivera appen"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Behörigheter tas bort av säkerhetsskäl från den här appen om den inte används på några månader."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Följande behörigheter tas bort av säkerhetsskäl från appen om den inte används på några månader: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Behörigheter har tagits bort av säkerhetsskäl från appar som inte har använts på några månader."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Tillåts att hantera alla filer"</string>
<string name="ask_header" msgid="2633816846459944376">"Fråga varje gång"</string>
<string name="denied_header" msgid="903209608358177654">"Tillåts inte"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> på <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Se fler appar som kan komma åt alla filer"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 dag}other{# dagar}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# timme}other{# timmar}}"</string>
@@ -355,7 +361,7 @@
<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 +381,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>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Anteckningsapp"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Appar som låter dig göra anteckningar på enheten"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"anteckningar"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Standardplånboksapp"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Plånboksapp"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Plånboksappar kan lagra dina kredit- och stamkundskort, bilnycklar och andra saker som hjälper till med olika sorters transaktioner."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Vill du ställa in <xliff:g id="APP_NAME">%1$s</xliff:g> som din standardplånboksapp?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Inga behörigheter krävs"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Nuvarande standardapp"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Fråga inte igen"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Ange som standard"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Fler standardappar"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Öppna länkar"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Standardinställning för jobbet"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Standard för privat område"</string>
<string name="default_app_none" msgid="9084592086808194457">"Ingen"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Systemstandard)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Inga appar"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Visa när enheten lyssnar efter ord som aktiverar assistenten"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Visa en ikon i statusfältet när mikrofonen används för att aktivera röstassistenten"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till foton och media på enheten?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton och media på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till dina kontakter?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till dina kontakter på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till enhetens plats?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till platsen för &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Appen får endast åtkomst till din plats när du använder den"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till enhetens plats?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till platsen för &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Appen kanske vill få åtkomst till din plats hela tiden, även när du inte använder den. "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Vill du ändra platsåtkomsten för &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Vill du ändra platsåtkomsten för &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Appen vill få åtkomst till din plats hela tiden, även när du inte använder den. "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Vill du tillåta att &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kan hitta, ansluta till och fastställa relativ position för enheter i närheten?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att hitta, ansluta till och avgöra relativ plats för enheter i närheten på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Vill du tillåta att &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kan hitta, ansluta till och fastställa relativ position för enheter i närheten? "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Vill du ändra platsåtkomsten för <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> från ungefärlig till exakt?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Vill du ändra platsåtkomsten för <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; från ungefärlig till exakt?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till enhetens ungefärliga plats?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till den ungefärliga platsen för &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Exakt"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Ungefärlig"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till din kalender?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till din kalender på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att skicka och visa sms?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att skicka och visa sms på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton, mediefiler och andra filer på enheten?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton, media och filer på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till &lt;b&gt;foton, videor, musik och ljud&lt;/b&gt; på enheten?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till &lt;b&gt;foton, videor, musik, ljud och andra filer&lt;/b&gt; på enheten?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till musik och ljud på enheten?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till musik och ljud på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton och videor på enheten?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till foton och videor på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till fler foton och videor på enheten?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till fler bilder och videor på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att spela in ljud?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att spela in ljud på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Appen kan bara spela in ljud medan du använder den"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att spela in ljud?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att spela in ljud på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Appen kanske vill spela in ljud hela tiden, även när du inte använder appen. "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Vill du ändra mikrofonåtkomsten för &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Vill du ändra mikrofonåtkomsten för &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Appen vill ha behörighet att spela in ljud hela tiden, även när du inte använder appen. "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till data om fysisk aktivitet?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till din fysiska aktivitet på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att ta bilder och spela in video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att ta bilder och spela in video på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Appen kan bara ta bilder och spela in video medan du använder den"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att ta bilder och spela in video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att ta bilder och spela in video på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Appen kanske vill ta bilder och spela in videor hela tiden, även när du inte använder appen. "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Vill du ändra kameraåtkomsten för &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Vill du ändra kameraåtkomsten för &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Appen vill ha behörighet att ta bilder och spela in videor hela tiden, även när du inte använder appen. "<annotation id="link">"Tillåt i inställningarna."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till samtalsloggarna?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till dina samtalsloggar på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att ringa och hantera telefonsamtal?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att ringa och hantera samtal på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till sensordata om vitalparametrar?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till sensordata om dina vitalparametrar på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Appen vill ha åtkomst till sensordata om vitalparametrar hela tiden, även när du inte använder den. "<annotation id="link">"Öppna inställningarna"</annotation>" om du vill tillåta detta."</string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomstbehörighet till sensordata om vitalparametrar?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till sensordata om dina vitalparametrar på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501"><annotation id="link">"Öppna inställningarna"</annotation>" om du vill ge appen åtkomst till data från kroppssensorer hela tiden, även när du inte använder appen."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Ska &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ha åtkomst till data från kroppssensorer medan appen används även i fortsättningen?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Vill du fortsätta ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; åtkomst till kroppssensordata på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; medan appen används?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Vill du ge &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; behörighet att skicka aviseringar?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Vill du tillåta &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; att skicka dig aviseringar på &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Styrda behörigheter"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> har platsåtkomst"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Din organisation tillåter att <xliff:g id="APP_NAME">%1$s</xliff:g> får åtkomst till din plats"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Inga"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Senaste\n24 timmarna"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Senaste\n7 dagarna"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> procent"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> skyddas av Android. Appens användning av behörigheter visas inte i statusfältet eller integritetsöversikten eftersom din data bearbetas på enheten."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> skyddas av Android. Appens användning av behörigheter visas inte i integritetsöversikten eftersom din data bearbetas på enheten."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Enhetens kamera är blockerad"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"För appar och tjänster"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofondata kan fortfarande delas när du ringer ett nödnummer."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Ändra"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kameraåtkomst är inaktiverad"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofonåtkomst är inaktiverad"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Platsåtkomst är inaktiverad"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"För infotainment-appar"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"För appar som krävs"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Den här appen krävs"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Den här appen krävs av bilens tillverkare"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Säkerhet och integritet"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skanna enhet"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Stäng"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Uppdateringar av datadelning"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"En del appar har ändrat hur de kan dela din platsdata"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Inställningar"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Öppnades <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Öppnades i går <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Öppnades <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Ditt engångslösenord är 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Begränsad inställning"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Av säkerhetsskäl är den här inställningen inte tillgänglig för närvarande."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Appen nekades åtkomst till <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Appen begärde åtkomstbehörighet till känsliga uppgifter, vilket kan utsätta din personliga och finansiella information för risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen kanske inte fungerar som den ska utan denna begränsade behörighet. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtLäs mer om hur du tillåter åtkomst&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Appen nekades åtkomst till standardrollen för <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Appen begärde åtkomstbehörigheter till känsliga uppgifter, vilket kan utsätta din personliga och finansiella information för risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen kanske inte fungerar som den ska utan dessa begränsade behörigheter. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtLäs mer om hur du tillåter åtkomst&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Appen nekades åtkomst"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Åtkomst till denna behörighet kan utsätta din personliga och finansiella information för risk.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Appen kanske inte fungerar som den ska utan denna begränsade behörighet. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&amp;gtLäs mer om hur du tillåter åtkomst&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Läs mer"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Begäran om behörighet har dolts"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Appen begär ytterligare behörigheter, men det går inte att bevilja behörigheter under streamingsessionen. Bevilja behörigheten på telefonen först."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"För ett nödsamtal eller nöd-sms"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Plats skickad till räddningstjänsten"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Den här appen fick åtkomst till enhetens plats när du ringde eller sms:ade till ett nödnummer. Detta kan hända även när appen inte har platsbehörighet eller om enhetens plats är inaktiverad. "<a href="https://support.google.com/android/answer/9319337">"Läs mer"</a></string>
</resources>
diff --git a/PermissionController/res/values-sw-v34/strings.xml b/PermissionController/res/values-sw-v34/strings.xml
index 52a1c7ceb..fe0aacbb6 100644
--- a/PermissionController/res/values-sw-v34/strings.xml
+++ b/PermissionController/res/values-sw-v34/strings.xml
@@ -17,11 +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">"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 namba ya dharura"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Kwenye programu na huduma"</string>
</resources>
diff --git a/PermissionController/res/values-sw-watch/strings.xml b/PermissionController/res/values-sw-watch/strings.xml
index b23b8b022..e6eb0999d 100644
--- a/PermissionController/res/values-sw-watch/strings.xml
+++ b/PermissionController/res/values-sw-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Haiwezi kubadilishwa"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ndiyo"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Ghairi"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Muda wote"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Unapotumia programu"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Muda wote"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Unapotumia programu"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Muda wote"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Unapotumia programu"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Muda wote"</string>
</resources>
diff --git a/PermissionController/res/values-sw/strings.xml b/PermissionController/res/values-sw/strings.xml
index 79b3164d2..194157a8e 100644
--- a/PermissionController/res/values-sw/strings.xml
+++ b/PermissionController/res/values-sw/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ruhusa"</string>
<string name="cancel" msgid="8943320028373963831">"Ghairi"</string>
<string name="back" msgid="6249950659061523680">"Rudi nyuma"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Funga"</string>
<string name="available" msgid="6007778121920339498">"Inaruhusiwa kufikia"</string>
<string name="blocked" msgid="9195547604866033708">"Imezuiwa kufikia"</string>
<string name="on" msgid="280241003226755921">"Imewashwa"</string>
@@ -34,6 +35,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,6 +62,7 @@
<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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Ruhusa zote"</string>
<string name="other_permissions" msgid="2901186127193849594">"Uwezo mwingine wa programu"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Ombi la idhini"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Huduma ya Android Wear haiwezi kutekeleza vitendo vya Kusakinisha au Kuondoa vipengee."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Chagua vipengee ambavyo unaruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; imesasishwa. Chagua vipengee unavyoruhusu programu hii ifikie."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Ghairi"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Ruhusu zote kila wakati"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Uliza kila wakati"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Usiruhusu"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Ruhusu ufikiaji wa baadhi ya vipengele"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Eneo mahususi"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Mahali palipokadiriwa"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Tumia eneo mahususi"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Wakati umezima eneo mahususi, programu zinaweza kufikia eneo lako lililokadiriwa"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Ruhusa ya <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Idhini ya programu hii kufikia <xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Ufikaiji wa <xliff:g id="PERM">%1$s</xliff:g> katika programu hii kwenye <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Angalia ruhusa zote za <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Angalia programu zote zenye ruhusa hii"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Onyesha matumizi ya maikrofoni ya mratibu"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Ondoa ruhusa ikiwa programu haitumiki"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Ondoa ruhusa na upate nafasi"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Simamisha shughuli kwenye programu ikiwa haitumiki"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Dhibiti programu iwapo haitumiki"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Ondoa ruhusa, futa faili za muda na usitishe arifa"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Ondoa ruhusa, futa faili za muda, komesha arifa na uweke programu kwenye kumbukumbu"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Ili kulinda data yako, ruhusa za programu hii zitaondolewa programu isipotumika kwa miezi michache."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ili kulinda data yako, programu isipotumika kwa miezi michache, ruhusa zifuatazo zitaondolewa: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Ili kulinda data yako, ruhusa zimeondolewa kwenye programu ambazo hujatumia kwa miezi michache."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Zinazoruhusiwa kudhibiti faili zote"</string>
<string name="ask_header" msgid="2633816846459944376">"Uliza kila wakati"</string>
<string name="denied_header" msgid="903209608358177654">"Zisizoruhusiwa"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> kwenye <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Angalia programu zaidi zinazoweza kufikia faili zote"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{Siku moja}other{Siku #}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{Saa #}other{Saa #}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Programu ya madokezo"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Programu zinazokuruhusu uandike madokezo kwenye kifaa chako"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"madokezo"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Programu chaguomsingi ya pochi"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Programu ya pochi"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Unaweza kuhifadhi kadi zako za mikopo na za kutuza uaminifu, funguo za magari na mengine kwenye programu za Pochi ili kukusaidia kufanya miamala mbalimbali."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Ungependa kuweka <xliff:g id="APP_NAME">%1$s</xliff:g> iwe programu yako chaguomsingi ya pochi?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Hakuna ruhusa zinazohitajika"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Chaguomsingi ya sasa"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Isiniulize tena"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Weka iwe chaguomsingi"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Chaguomsingi zaidi"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Kufungua viungo"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Programu chaguomsingi kazini"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Programu chaguomsingi za sehemu ya faragha"</string>
<string name="default_app_none" msgid="9084592086808194457">"Hakuna"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Programu chaguomsingi ya mfumo)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Hakuna programu"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Onyesha utambuzi wa kuwashwa/kuzimwa kwa mratibu"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Onyesha aikoni kwenye sehemu ya kuonyesha hali wakati maikrofoni inatumika kuwasha kipengele cha maagizo ya sauti"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na maudhui kwenye kifaa chako?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na maudhui kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie anwani zako?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie anwani zako kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie mahali kilipo kifaa hiki?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie maelezo ya mahali &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ilipo?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Programu itafikia data ya mahali ulipo unapoitumia tu"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie mahali kilipo kifaa hiki?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie maelezo ya mahali &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ilipo?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Huenda programu hii ikataka kufikia maelezo ya mahali ulipo kila wakati, hata wakati huitumii."<annotation id="link">"Iruhusu katika mipangilio."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Ungependa kubadilisha ruhusa za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; za kufikia maelezo ya mahali?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Ungependa kubadilisha idhini ya kufikia maelezo ya mahali ya &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Programu hii inataka kufikia maelezo ya mahali ulipo kila wakati, hata wakati huitumii."<annotation id="link">"Iruhusu katika mipangilio."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itafute, iunganishe kwenye na ibaini mahali vilipo vifaa vilivyo karibu?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itafute, iunganishe na kubaini nafasi ya makadirio ya vifaa vilivyo karibu kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itafute, iunganishe kwenye na ibaini mahali vilipo vifaa vilivyo karibu? "<annotation id="link">"Ruhusu katika mipangilio."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Ungependa kubadilisha ufikiaji wa maelezo ya mahali kwenye <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> kutoka eneo lililokadiriwa utumie eneo mahususi?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Ungependa kubadilisha idhini ya <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ya kufikia maelezo ya mahali kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; kutoka makadirio kuwa mahususi?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie mahali palipokadiriwa kilipo kifaa hiki?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie eneo linalokadiriwa la &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Mahususi"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Palipokadiriwa"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie kalenda yako?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie kalenda yako kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; itume na ione ujumbe wa SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie na itume ujumbe wa SMS kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Ungependa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha, maudhui na faili kwenye kifaa chako?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha, maudhui na faili kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie &lt;b&gt;picha, video, muziki na sauti&lt;/b&gt; kwenye kifaa hiki?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie &lt;b&gt;picha, video, muziki, sauti na faili zingine&lt;/b&gt; kwenye kifaa hiki?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie muziki na sauti kwenye kifaa hiki?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie muziki na sauti kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na video kwenye kifaa hiki?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na video kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na video zaidi kwenye kifaa hiki?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie picha na video zaidi kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kurekodi sauti?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; irekodi sauti kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Programu itaweza kurekodi sauti unapoitumia tu"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; irekodi sauti?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; irekodi sauti kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Huenda programu hii ikataka kurekodi sauti kila wakati, hata wakati huitumii."<annotation id="link">"Ruhusu katika mipangilio."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Ungependa kubadilisha ruhusa za &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; za kufikia maikrofoni?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Ungependa kubadilisha idhini ya &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ya kufikia maikrofoni kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Programu hii inataka kurekodi sauti kila wakati, hata wakati huitumii."<annotation id="link">"Ruhusu katika mipangilio."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie shughuli zako za kimwili?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie shughuli yako ya mazoezi ya mwili kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kupiga picha na kurekodi video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ipige picha na kurekodi video kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Programu itaweza kupiga picha na kurekodi video unapoitumia tu"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ipige picha na kurekodi video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ipige picha na kurekodi video kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Ungependa kubadilisha idhini ya kufikia kamera ya &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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 namba za simu?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie rekodi zako za namba za simu kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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_device_aware_phone" msgid="590399263670349955">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ipige na kudhibiti simu kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya vitambuzi inayohusu viashiria muhimu vya mwili wako kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya vitambuzi inayohusu viashiria muhimu vya mwili wako?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya vitambuzi inayohusu viashiria muhimu vya mwili wako kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Ili uruhusu programu hii ifikie data ya vitambuzi vya shughuli za mwili kila wakati, hata wakati hutumii programu, "<annotation id="link">"nenda kwenye mipangilio."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Ungependa kuendelea kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya vitambuzi vya shughuli za mwili wakati programu inatumika?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Ungependa kuendelea kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifikie data ya vitambuzi vya shughuli za mwili kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; wakati programu inatumika?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Ungependa kuruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ikutumie arifa?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Ungependa kuiruhusu &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ikutumie arifa kwenye &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Ruhusa zinazodhibitiwa"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> ina idhini ya kufikia maelezo ya mahali kilipo kifaa"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Shirika lako limeruhusu <xliff:g id="APP_NAME">%1$s</xliff:g> kufikia maelezo ya mahali kilipo kifaa chako"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Hamna"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Saa 24\nzilizopita"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Siku\nsaba zilizopita"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> asilimia <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> inalindwa na Android. Kwa sababu data yako inachakatwa kwenye kifaa hiki, matumizi ya ruhusa ya programu hii hayaonyeshwi kwenye sehemu ya kuonyesha hali au dashibodi yako ya faragha."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> inalindwa na Android. Kwa sababu data yako inachakatwa kwenye kifaa hiki, matumizi ya ruhusa ya programu hii hayaonyeshwi kwenye dashibodi yako ya faragha."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Kamera ya kifaa imezuiwa"</string>
@@ -520,7 +560,14 @@
<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 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="automotive_blocked_camera_title" msgid="6142362431548829416">"Idhini ya kufikia kamera imezimwa"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Umezima uwezo wa kufikia maikrofoni"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Umezima uwezo wa kufikia maelezo ya mahali"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Kwa programu za habari na burudani"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Kwa programu zinazotakiwa kuwepo"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Programu hii inatakikana"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Mtengenezaji wa gari lako anataka programu hii iwepo"</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 +578,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>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Masasisho ya kushiriki data"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Baadhi ya programu zimebadilisha jinsi zinavyoweza kushiriki data ya mahali ulipo"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Mipangilio"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Kilifunguliwa <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Kilifunguliwa jana <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Kilifunguliwa <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Nenosiri lako la wakati mmoja ni 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Mipangilio imezuiwa"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ili kulinda usalama wako, mipangilio hii haipatikani kwa sasa."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Programu haijapewa idhini ya kufikia <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Programu imeomba kufikia ruhusa nyeti, hali ambayo inaweza kuhatarisha maelezo yako ya kifedha na ya binafsi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kuna uwezekano kuwa programu haitafanya kazi vizuri bila ruhusa hii inayodhibitiwa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Pata maelezo kuhusu jinsi ya kuruhusu ufikiaji&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Programu haijapewa idhini ya kuwa <xliff:g id="ROLE_NAME">%1$s</xliff:g> chaguomsingi"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Programu imeomba kufikia ruhusa nyeti, hali ambayo inaweza kuhatarisha taarifa zako za kifedha na za binafsi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kuna uwezekano kuwa programu haitafanya kazi vizuri bila ruhusa hii inayodhibitiwa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Pata maelezo kuhusu jinsi ya kuruhusu ufikiaji&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Programu haijapewa idhini"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Ufikiaji wa ruhusa hii unaweza kuhatarisha taarifa zako za kifedha na za binafsi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kuna uwezekano kuwa programu haitafanya kazi vizuri bila ruhusa hii inayodhibitiwa. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Pata maelezo kuhusu jinsi ya kuruhusu ufikiaji&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Pata maelezo zaidi"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Sawa"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Ombi la ruhusa limezuiwa"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Programu hii inaomba ruhusa za ziada. Hata hivyo, huwezi kutoa ruhusa ukitiririsha. Ruhusu kwenye simu yako kwanza."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Kwa kutuma ujumbe au kupiga simu ya dharura"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Data ya mahali imetumwa kwa huduma za dharura"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Programu hii ilifikia data ya mahali kilipo kifaa chako ulipokuwa ukipiga simu au kutuma ujumbe kwa namba ya dharura. Hali hii inaweza kutokea hata kama programu haina ruhusa ya mahali au umezima kipengele cha kufikia data ya mahali kifaa kilipo. "<a href="https://support.google.com/android/answer/9319337">"Pata maelezo zaidi"</a></string>
</resources>
diff --git a/PermissionController/res/values-ta-v34/strings.xml b/PermissionController/res/values-ta-v34/strings.xml
index b92b9a184..593225811 100644
--- a/PermissionController/res/values-ta-v34/strings.xml
+++ b/PermissionController/res/values-ta-v34/strings.xml
@@ -23,5 +23,4 @@
<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">"ஆப்ஸ் &amp; சேவைகளுக்கு"</string>
</resources>
diff --git a/PermissionController/res/values-ta-watch/strings.xml b/PermissionController/res/values-ta-watch/strings.xml
index 8ba5ec786..51073c144 100644
--- a/PermissionController/res/values-ta-watch/strings.xml
+++ b/PermissionController/res/values-ta-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"மாற்ற இயலாது"</string>
<string name="generic_yes" msgid="2489207724988649846">"சரி"</string>
<string name="generic_cancel" msgid="2631708607129269698">"வேண்டாம்"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"எப்போதும்"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ஆப்ஸைப் பயன்படுத்தும்போது"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"எப்போதும்"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ஆப்ஸைப் பயன்படுத்தும்போது"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"எப்போதும்"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ஆப்ஸைப் பயன்படுத்தும்போது"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"எப்போதும்"</string>
</resources>
diff --git a/PermissionController/res/values-ta/strings.xml b/PermissionController/res/values-ta/strings.xml
index ad2bea8a3..f4120e3d8 100644
--- a/PermissionController/res/values-ta/strings.xml
+++ b/PermissionController/res/values-ta/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"அனுமதிகள்"</string>
<string name="cancel" msgid="8943320028373963831">"ரத்துசெய்"</string>
<string name="back" msgid="6249950659061523680">"பின்செல்"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"மூடு"</string>
<string name="available" msgid="6007778121920339498">"உள்ளது"</string>
<string name="blocked" msgid="9195547604866033708">"தடுக்கப்பட்டது"</string>
<string name="on" msgid="280241003226755921">"இயக்கப்பட்டுள்ளது"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"நிறுவல்/நிறுவல் நீக்குதலை Wearரில் செய்ய இயலாது."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; எவற்றையெல்லாம் அணுகலாம் என்பதைத் தேர்வுசெய்யவும்"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; புதுப்பிக்கப்பட்டது. இந்த ஆப்ஸ் எவற்றையெல்லாம் அணுகலாம் என்பதைத் தேர்வுசெய்யவும்."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"வேண்டாம்"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"வரம்பிற்குட்பட்ட அணுகலை அனுமதித்தல்"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> சாதனத்தில் இந்த ஆப்ஸுக்கான <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"ஆப்ஸைப் பயன்படுத்தாதபோது நிர்வகித்தல்"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"அனுமதிகளை அகற்றும், தற்காலிக ஃபைல்களை நீக்கும், அறிவிப்புகளை நிறுத்தும்"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"அனுமதிகளை அகற்றும், தற்காலிக ஃபைல்களை நீக்கும், அறிவிப்புகளை நிறுத்தும், ஆப்ஸைக் காப்பிடும்"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"இந்த ஆப்ஸைச் சில மாதங்கள் பயன்படுத்தவில்லை என்றால் உங்கள் தரவைப் பாதுகாப்பதற்காக இதற்கான அனுமதிகள் அகற்றப்படும்."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"இந்த ஆப்ஸைச் சில மாதங்கள் பயன்படுத்தவில்லை என்றால் உங்கள் தரவைப் பாதுகாப்பதற்காக பின்வரும் அனுமதிகள் அகற்றப்படும்: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"உங்கள் தரவைப் பாதுகாப்பதற்காக, கடந்த சில மாதங்களில் நீங்கள் பயன்படுத்தாத ஆப்ஸில் இருந்து அனுமதிகள் அகற்றப்பட்டன."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"எல்லா ஃபைல்களையும் நிர்வகிக்கும் அணுகலுள்ளவை"</string>
<string name="ask_header" msgid="2633816846459944376">"ஒவ்வொரு முறையும் கேள்"</string>
<string name="denied_header" msgid="903209608358177654">"அனுமதிக்கப்படாதவை"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> சாதனத்தில் <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"அனைத்து ஃபைல்களையும் அணுகக்கூடிய கூடுதல் ஆப்ஸைப் பாருங்கள்"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 நாள்}other{# நாட்கள்}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# மணிநேரம்}other{# மணிநேரம்}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"இயல்புநிலை வாலட் ஆப்ஸ்"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"வாலட் ஆப்ஸ்"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"பல்வேறு வகையான பரிமாற்றங்கள் தொடர்பாக உங்களுக்கு உதவ கிரெடிட் கார்டுகள், லாயல்டி கார்டுகள், கார் சாவிகள் மற்றும் பிறவற்றை வாலட் ஆப்ஸில் சேமிக்கலாம்."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை உங்கள் இயல்புநிலை வாலட் ஆப்ஸாக அமைக்கவா?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"அனுமதிகள் தேவையில்லை"</string>
<string name="request_role_current_default" msgid="738722892438247184">"தற்போதைய இயல்பான ஆப்ஸ்"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"மீண்டும் கேட்காதே"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"இயல்பு ஆப்ஸாக அமை"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"கூடுதல் இயல்புநிலைகள்"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"இணைப்புகளைத் திறத்தல்"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"பணிக்கான இயல்பு நிலை ஆப்ஸ்"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ரகசிய இடத்திற்கான இயல்பு"</string>
<string name="default_app_none" msgid="9084592086808194457">"ஏதுமில்லை"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(சிஸ்டத்தின் இயல்புநிலை)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ஆப்ஸ் இல்லை"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் படங்கள் மற்றும் மீடியாவிற்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"தொடர்புகளை அணுகுவதற்கு &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உங்கள் தொடர்புகளுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"இந்தச் சாதன இருப்பிடத்தை அணுகுவதற்கு &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தின் இருப்பிடத்திற்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தின் இருப்பிடத்திற்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கான இருப்பிட அணுகலை மாற்றவா?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"இந்த ஆப்ஸை நீங்கள் பயன்படுத்தாதபோதும்கூட உங்கள் இருப்பிடத்தை எந்நேரமும் அணுக இது விரும்பக்கூடும். "<annotation id="link">"அமைப்புகளில் அனுமதிக்கவும்"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"அருகிலுள்ள சாதனங்களைக் கண்டறியவும் அவற்றுடன் இணையவும் அவற்றின் தூரத்தைத் தீர்மானிக்கவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தின் அருகிலுள்ள சாதனங்களைக் கண்டறியவும் அவற்றுடன் இணையவும் அவற்றின் தூரத்தைத் தீர்மானிக்கவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"அருகிலுள்ள சாதனங்களைக் கண்டறியவும் அவற்றுடன் இணையவும் அவற்றின் தூரத்தைத் தீர்மானிக்கவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா? "<annotation id="link">"அமைப்புகளில் அனுமதி."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ஆப்ஸின் இருப்பிட அணுகலைத் தோராயத்திலிருந்து துல்லியத்திற்கு மாற்றவா?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் <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_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தின் தோராய இருப்பிடத்திற்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உங்கள் கேலெண்டருக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"மெசேஜ்களை அனுப்பவும், பார்க்கவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் மெசேஜ்களை அனுப்பவும் பார்க்கவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"உங்கள் சாதனத்திலுள்ள படங்கள், மீடியா, ஃபைல்கள் ஆகியவற்றை அணுகுவதற்கு &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் படங்கள், மீடியா, ஃபைல்கள் ஆகியவற்றுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"சாதனத்திலுள்ள &lt;b&gt;படங்கள், வீடியோக்கள், இசை &amp; ஆடியோவுக்கான&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;படம், வீடியோ, இசை, ஆடியோ &amp; பிற ஃபைல்களின்&lt;/b&gt; அணுகலை &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் இசை மற்றும் ஆடியோவுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் படங்கள் மற்றும் வீடியோக்களுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"இந்தச் சாதனத்திலுள்ள கூடுதல் படங்கள் மற்றும் வீடியோக்களுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் கூடுதல் படங்கள் மற்றும் வீடியோக்களுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"ஆடியோ ரெக்கார்டு செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் ஆடியோவை ரெக்கார்டு செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் ஆடியோவை ரெக்கார்டு செய்ய &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கான மைக்ரோஃபோன் அணுகலை மாற்றவா?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உங்கள் உடல் செயல்பாட்டிற்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"படங்கள் எடுக்கவும் வீடியோ ரெக்கார்டு செய்யவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் படங்களை எடுக்கவும் வீடியோவை ரெக்கார்டு செய்யவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் படங்களை எடுக்கவும் வீடியோவை ரெக்கார்டு செய்யவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கான கேமரா அணுகலை மாற்றவா?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உங்கள் மொபைல் அழைப்புப் பதிவுகளுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"மொபைல் அழைப்புகள் செய்யவும், அவற்றை நிர்வகிக்கவும், &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் மொபைல் அழைப்புகளைச் செய்யவும் அவற்றை நிர்வகிக்கவும் &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"உங்கள் உடலியக்கக் குறிகள் பற்றிய சென்சார் தரவை அணுகுவதற்கு &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உடல் இயக்க அளவீடுகள் பற்றிய சென்சார் தரவுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உடல் இயக்க அளவீடுகள் பற்றிய சென்சார் தரவுக்கான அணுகலை &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸுக்கு வழங்கவா?"</string>
<string name="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&amp;gt ஆப்ஸுக்குத் தொடர்ந்து அனுமதியளிக்கவா?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸ் உபயோகத்தில் இருக்கும்போது &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; சாதனத்தில் உங்களுக்கு அறிவிப்புகளை அனுப்ப &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"எதுவுமில்லை"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"கடந்த\n24 மணிநேரம்"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"கடந்த\n7 நாட்கள்"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> சதவீதம்"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் Android மூலம் பாதுகாக்கப்படுகிறது. இந்தச் சாதனத்தில் உங்கள் தரவு செயலாக்கப்படுவதால் நிலைப் பட்டியிலோ தனியுரிமை டாஷ்போர்டிலோ இந்த ஆப்ஸின் அனுமதி உபயோகம் காட்டப்படவில்லை."</string>
<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>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"ஆப்ஸ் &amp; சேவைகளுக்கு"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"எனினும் அவசர உதவி எண்ணைத் தொடர்பு கொள்ளும்போது மைக்ரோஃபோன் தரவு பகிரப்படலாம்."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"மாற்று"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"கேமரா அணுகல் முடக்கப்பட்டுள்ளது"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"மைக்ரோஃபோன் அணுகல் முடக்கப்பட்டுள்ளது"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"இருப்பிட அணுகல் முடக்கப்பட்டுள்ளது"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"இன்ஃபோடெயின்மென்ட் ஆப்ஸுக்கு"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"தேவையான ஆப்ஸுக்கு"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"இந்த ஆப்ஸ் தேவை"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"இந்த ஆப்ஸ் உங்கள் காரின் உற்பத்தியாளருக்குத் தேவை"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"பாதுகாப்பு &amp; தனியுரிமை"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"சாதனத்தை ஸ்கேன் செய்"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"நிராகரிக்கும்"</string>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"அணுகிய நேரம்: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"நேற்று அணுகிய நேரம்: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"அணுகிய நேரம்: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"ஒருமுறை பயன்படுத்தப்படும் கடவுச்சொல்: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"கட்டுப்படுத்தப்பட்ட அமைப்பு"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"உங்கள் பாதுகாப்பிற்காக, இந்த அமைப்பு தற்போது இல்லை."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>க்கான ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"பாதுகாக்க வேண்டிய தகவல்களுக்கான அனுமதியை வழங்க ஆப்ஸ் கேட்டுள்ளது, இது உங்கள் தனிப்பட்ட மற்றும் நிதித் தகவல்களை ஆபத்துக்குள்ளாக்கலாம்.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>இந்தக் கட்டுப்படுத்தப்பட்ட அனுமதி இல்லாமல் ஆப்ஸ் சரியாக வேலை செய்யாமல் போகக்கூடும். &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;எப்படி அணுகல் வழங்குவதென அறிக&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"இயல்புநிலை <xliff:g id="ROLE_NAME">%1$s</xliff:g>க்கான ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"பாதுகாக்க வேண்டிய தகவலை அணுவதற்கான அனுமதியை ஆப்ஸ் கேட்டுள்ளது, இது உங்கள் தனிப்பட்ட மற்றும் நிதித் தகவல்களை ஆபத்துக்குள்ளாக்கலாம்.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>இந்தக் கட்டுப்படுத்தப்பட்ட அனுமதிகள் இல்லாமல் ஆப்ஸ் சரியாக வேலை செய்யாமல் போகக்கூடும். &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;எப்படி அணுகல் வழங்குவதென அறிக&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ஆப்ஸ் அணுகல் நிராகரிக்கப்பட்டது"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"இதற்கு அனுமதி அளிப்பதனால் உங்கள் தனிப்பட்ட மற்றும் நிதித் தகவல்கள் ஆபத்துக்குள்ளாகலாம்.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>இந்தக் கட்டுப்படுத்தப்பட்ட அனுமதி இல்லாமல் ஆப்ஸ் சரியாக வேலை செய்யாமல் போகக்கூடும். &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;எப்படி அணுகல் வழங்குவதென அறிக&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"மேலும் அறிக"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"சரி"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"அணுகல் கோரிக்கை முடக்கப்பட்டது"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"இந்த ஆப்ஸ் கூடுதல் அனுமதிகளைக் கேட்கிறது. ஆனால் ஸ்ட்ரீமிங் அமர்வில் அனுமதிகள் வழங்கப்படாது. முதலில் உங்கள் மொபைலில் அனுமதி வழங்கவும்."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"அவசர அழைப்பு/மெசேஜுக்கு"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"அவசரகாலச் சேவைகளுக்கு இருப்பிடம் அனுப்பப்பட்டது"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"அவசர உதவி எண்ணை அழைத்தபோதோ அந்த எண்ணிற்கு மெசேஜ் அனுப்பியபோதோ உங்கள் சாதனத்தின் இருப்பிடத்தை இந்த ஆப்ஸ் அணுகியது. இருப்பிட அனுமதி ஆப்ஸுக்கு இல்லாதபோதும் சாதனத்தின் இருப்பிடம் முடக்கப்பட்டிருக்கும்போதும் கூட இப்படி நிகழலாம். "<a href="https://support.google.com/android/answer/9319337">"மேலும் அறிக"</a></string>
</resources>
diff --git a/PermissionController/res/values-te-v34/strings.xml b/PermissionController/res/values-te-v34/strings.xml
index c8281ea1d..f3df7f7d3 100644
--- a/PermissionController/res/values-te-v34/strings.xml
+++ b/PermissionController/res/values-te-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-te-watch/strings.xml b/PermissionController/res/values-te-watch/strings.xml
index dfd5af38f..57f32b8d3 100644
--- a/PermissionController/res/values-te-watch/strings.xml
+++ b/PermissionController/res/values-te-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"మార్చడం సాధ్యపడదు"</string>
<string name="generic_yes" msgid="2489207724988649846">"అవును"</string>
<string name="generic_cancel" msgid="2631708607129269698">"రద్దు చేయండి"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"అన్ని సమయాలలో అనుమతించండి"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"యాప్‌ను వాడుతున్నప్పుడు"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"అన్ని సమయాలలో అనుమతించండి"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"యాప్‌ను వాడుతున్నప్పుడు"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"అన్ని సమయాలలో అనుమతించండి"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"యాప్‌ను వాడుతున్నప్పుడు"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"అన్ని సమయాలలో అనుమతించండి"</string>
</resources>
diff --git a/PermissionController/res/values-te/strings.xml b/PermissionController/res/values-te/strings.xml
index 221e5cd04..a0211b57a 100644
--- a/PermissionController/res/values-te/strings.xml
+++ b/PermissionController/res/values-te/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"అనుమతులు"</string>
<string name="cancel" msgid="8943320028373963831">"రద్దు చేయండి"</string>
<string name="back" msgid="6249950659061523680">"వెనుకకు"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"మూసివేయండి"</string>
<string name="available" msgid="6007778121920339498">"అందుబాటులో ఉంది"</string>
<string name="blocked" msgid="9195547604866033708">"బ్లాక్ చేయబడినది"</string>
<string name="on" msgid="280241003226755921">"ఆన్‌లో ఉంది"</string>
@@ -34,6 +35,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 +62,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 +72,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>
@@ -114,8 +117,6 @@
<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 వేర్"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wearలో ఇన్‌స్టాల్/అన్ఇన్‌స్టాల్ చర్యలకు మద్దతు లేదు."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాక్సెస్ చేయడానికి అనుమతించాల్సిన వాటిని ఎంచుకోండి"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; అప్‌డేట్ చేయబడింది. ఈ యాప్ యాక్సెస్ చేయడానికి అనుమతించాల్సిన వాటిని ఎంచుకోండి."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"రద్దు చేయండి"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"పరిమిత యాక్సెస్‌ను అనుమతించండి"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> పరికరంలో ఈ యాప్‌నకు <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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"ఉపయోగించని యాప్‌ను మేనేజ్ చేయండి"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"అనుమతులను తీసివేయండి, తాత్కాలిక ఫైళ్లను తొలగించండి, అలాగే నోటిఫికేషన్‌లను ఆపివేయండి"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"అనుమతులను తీసివేయండి, తాత్కాలిక ఫైళ్లను తొలగించండి, నోటిఫికేషన్‌లను ఆపివేయండి, యాప్‌ను ఆర్కైవ్ చేయండి"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"మీ డేటాను సురక్షితంగా ఉంచడానికి, ఈ యాప్ కొన్ని నెలలుగా వినియోగంలో లేకుంటే, దాని అనుమతులు తీసివేయబడతాయి."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"మీ డేటాను సురక్షితంగా ఉంచడానికి, ఈ యాప్ కొన్ని నెలలుగా వినియోగంలో లేకుంటే, దానికి ఇచ్చిన కింది అనుమతులు తీసివేయబడతాయి: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"మీ డేటాను సురక్షితంగా ఉంచడానికి, కొన్ని నెలలుగా వినియోగంలో లేని యాప్‌ల అనుమతులు తీసివేయబడ్డాయి."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ఫైల్స్ అన్నింటినీ మేనేజ్ చేసేందుకు అనుమతించబడినవి"</string>
<string name="ask_header" msgid="2633816846459944376">"ప్రతిసారి అడగాలి"</string>
<string name="denied_header" msgid="903209608358177654">"అనుమతించబడలేదు"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‌లో <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"అన్ని ఫైళ్లను యాక్సెస్ చేయగల మరిన్ని యాప్‌లను చూడండి"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 రోజు}other{# రోజులు}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# గంట}other{# గంటలు}}"</string>
@@ -352,7 +358,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ఆటోమేటిక్ వాలెట్ యాప్"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"వాలెట్ యాప్"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"వాలెట్ యాప్‌లు మీ క్రెడిట్‌ను, లాయల్టీ కార్డ్‌లను, కార్ కీలను, ఇంకా వివిధ రకాల లావాదేవీలకు సహాయపడటానికి ఇతర వస్తువులను స్టోర్ చేయగలవు."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g>‌ను డిఫాల్ట్ వాలెట్ యాప్‌గా సెట్ చేయాలా?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"అనుమతులు ఏవీ అవసరం లేదు"</string>
<string name="request_role_current_default" msgid="738722892438247184">"ప్రస్తుతం ఆటోమేటిక్‌గా ఉంది"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"మళ్లీ అడగవద్దు"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ఆటోమేటిక్ చేయండి"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"మరిన్ని ఆటోమేటిక్ సెట్టింగ్స్‌"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"లింక్‌లను తెరవడం"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"వర్క్‌ ప్లేస్ కోసం ఆటోమేటిక్"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ఆటోమేటిక్‌గా ప్రైవేట్ స్పేస్"</string>
<string name="default_app_none" msgid="9084592086808194457">"ఏదీ కాదు"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(సిస్టమ్ ఆటోమేటిక్)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ఏ యాప్ లేదు"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Assistant ట్రిగ్గర్ గుర్తింపును చూపించడం"</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_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోటోలు, మీడియాను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతినివ్వాలా?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"మీ కాంటాక్ట్‌లను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో మీ కాంటాక్ట్‌లను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"ఈ పరికర లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు సంబంధించిన లొకేషన్ యాక్సెస్‌ను మార్చాలా?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"మీరు యాప్ ఉపయోగించనప్పుడు కూడా ఈ యాప్ మీ లొకేష‌న్‌ను ఎప్పటికప్పుడు యాక్సెస్ చేయాల‌ని అనుకుంటోంది."<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
- <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి \"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;\"ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో సమీప పరికరాలు కనుగొని, కనెక్ట్ అయి, వాటి దూరం అంచనా వేసేలా &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"సమీప పరికరాల సంబంధిత స్థానాన్ని కనుగొనడానికి, కనెక్ట్ చేయడానికి అలాగే నిర్ణయించడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా? "<annotation id="link">"సెట్టింగ్‌లలో అనుమతించండి."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>కు సంబంధించిన లొకేషన్ యాక్సెస్‌ను సుమారు నుండి ఖచ్చితమైనదిగా మార్చాలా?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో &lt;b&gt;<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>&lt;/b&gt; లొకేషన్ యాక్సెస్‌ను రమారమి నుండి ఖచ్చితమైన లొకేషన్‌కు మార్చాలా?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"ఈ పరికరానికి సంబంధించి సుమారుగా ఉన్న లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; రమారమి లొకేషన్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో మీ క్యాలెండర్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"SMS మెసేజ్‌లు పంపడం, చూడటం చేయగలిగేలా &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో 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_device_aware_storage" msgid="6933251810928606636">"lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోటోలను, మీడియాను, ఫైల్స్‌ను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="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;ఫోటోలు, వీడియోలు, మ్యూజిక్, ఆడియో, ఇతర ఫైళ్ల&lt;/b&gt; యాక్సెస్‌కు &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_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో మ్యూజిక్‌ను, ఆడియోను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతినివ్వాలా?"</string>
<string name="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_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోటోలను, వీడియోలను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతినివ్వాలా?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"ఈ పరికరంలోని మరిన్ని ఫోటోలను, వీడియోలను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో మరిన్ని ఫోటోలను, వీడియోలను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలా?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"ఆడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఆడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఆడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు సంబంధించిన మైక్రోఫోన్ యాక్సెస్‌ను మార్చాలా?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫిజికల్ యాక్టివిటీని యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతించాలనుకుంటున్నారా?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"ఫోటోలు తీయడానికి, వీడియో రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోటోలను తీయడానికి, వీడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోటోలను తీయడానికి, వీడియోను రికార్డ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు సంబంధించిన కెమెరా యాక్సెస్‌ను మార్చాలా?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోన్ కాల్ లాగ్‌లను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌కు అనుమతించాలనుకుంటున్నారా?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"ఫోన్‌ కాల్స్‌ చేయడానికి, మేనేజ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో ఫోన్ కాల్స్ చేయడానికి, వాటిని మేనేజ్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"మీ అత్యంత కీలకమైన గుర్తుల గురించి సెన్సార్ డేటాను యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ని అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో కీలకమైన ఆరోగ్య కొలమానాల సెన్సార్ డేటా యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో కీలక ఆరోగ్య కొలమానాల సెన్సార్ డేటా యాక్సెస్ చేయడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలా?"</string>
<string name="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_device_aware_sensors" msgid="5542771499929819675">"యాప్ ఉపయోగంలో ఉన్నప్పుడు &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో శరీర సెన్సార్ డేటాకు &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; యాక్సెస్ ఇవ్వడం కొనసాగించాలా?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"మీకు నోటిఫికేషన్‌లను పంపడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ను అనుమతించాలా?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;‌లో మీకు నోటిఫికేషన్‌లను పంపడానికి &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ఏదీ కాదు"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"గత \n 24 గంటలు"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"గత\n7 రోజులు"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> శాతం"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Android ద్వారా <xliff:g id="APP_NAME">%1$s</xliff:g> సంరక్షించబడుతోంది. మీ డేటా ఈ పరికరంలో ప్రాసెస్ చేయబడుతున్నందున, ఈ యాప్‌నకు సంబంధించిన అనుమతి వినియోగ సమాచారం స్టేటస్ బార్‌లో లేదా మీ గోప్యతా డ్యాష్‌బోర్డ్‌లో చూపబడదు."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Android ద్వారా <xliff:g id="APP_NAME">%1$s</xliff:g> సంరక్షించబడుతోంది. మీ డేటా ఈ పరికరంలో ప్రాసెస్ చేయబడుతున్నందున, ఈ యాప్‌నకు సంబంధించిన అనుమతి వినియోగ సమాచారం మీ గోప్యతా డ్యాష్‌బోర్డ్‌లో చూపబడదు."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"పరికరం కెమెరా బ్లాక్ చేయబడింది"</string>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"కెమెరా యాక్సెస్ ఆఫ్‌లో ఉంది"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"మైక్రోఫోన్ యాక్సెస్ ఆఫ్‌లో ఉంది"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"లొకేషన్ యాక్సెస్ ఆఫ్‌లో ఉంది"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"సమాచారంతో కూడిన వినోదం యాప్‌ల కోసం"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"అవసరమైన యాప్‌ల కోసం"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"ఈ యాప్ అవసరం"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ఈ యాప్ మీ కారు తయారీదారుకు అవసరం"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"సెక్యూరిటీ &amp; గోప్యత"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"పరికరాన్ని స్కాన్ చేయండి"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"విస్మరించండి"</string>
@@ -581,9 +628,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>‌కు యాక్సెస్‌ చేశారు"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"నిన్న <xliff:g id="TIME_DATE">%1$s</xliff:g>‌కు యాక్సెస్‌ చేశారు"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g>‌న<xliff:g id="TIME_DATE_1">%2$s</xliff:g>‌కు యాక్సెస్‌ చేశారు"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"మీ ఒకసారి ఉపయోగించగల పాస్‌వర్డ్ 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"పరిమితం చేయబడిన సెట్టింగ్"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"మీ సెక్యూరిటీ కోసం, ఈ సెట్టింగ్ ప్రస్తుతం అందుబాటులో లేదు."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"యాప్ <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>‌కు యాక్సెస్ తిరస్కరించబడింది"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"మీ వ్యక్తిగత, ఆర్థిక సమాచారాన్ని ప్రమాదంలో పడేసే గోప్యమైన సమాచార యాక్సెస్ అనుమతికి యాప్ యాక్సెస్‌ను రిక్వెస్ట్ చేయడం జరిగింది.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ఈ అదనపు అవసరాలు గల అనుమతి లేకుండా యాప్ సరిగ్గా పని చేయకపోయే అవకాశం ఉంది. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;యాక్సెస్‌ను అనుమతించడం ఎలాగో తెలుసుకోండి&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ఆటోమేటిక్ <xliff:g id="ROLE_NAME">%1$s</xliff:g>‌గా ఉండటానికి యాప్‌నకు యాక్సెస్ నిరాకరించబడింది"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"మీ వ్యక్తిగత, ఆర్థిక సమాచారాన్ని ప్రమాదంలో పడేయగల గోప్యమైన సమాచారాన్ని యాక్సెస్ చేయడానికి అనుమతులు (సెన్సిటివ్ పర్మిషన్స్) కావాలని ఈ యాప్ కోరింది.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>అయితే ఈ అదనపు అవసరాలు గల అనుమతులను (రిస్ట్రిక్టెడ్ పర్మిషన్స్‌ను) ఇవ్వకపోతే యాప్ సరిగ్గా పని చేయకుండా పోయే అవకాశం ఉంది. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;యాక్సెస్‌ను అనుమతించడం ఎలాగో తెలుసుకోండి&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"యాప్‌నకు యాక్సెస్ తిరస్కరించబడింది"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"ఈ అనుమతికి యాక్సెస్‌ను ఇవ్వడం వల్ల మీ వ్యక్తిగత, ఆర్థిక సమాచారం ప్రమాదంలో పడవచ్చు.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>ఈ అదనపు అవసరాలు గల అనుమతులు లేకుండా యాప్ సరిగ్గా పని చేయకపోయే అవకాశం ఉంది. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;యాక్సెస్‌ను అనుమతించడం ఎలాగో తెలుసుకోండి&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"మరింత తెలుసుకోండి"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"సరే"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"అనుమతి రిక్వెస్ట్ బ్లాక్ చేయబడింది"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"ఈ యాప్ అదనపు అనుమతి కోసం రిక్వెస్ట్ చేస్తోంది, కానీ స్ట్రీమింగ్ సెషన్‌లో అనుమతులను మంజూరు చేయడం సాధ్యం కాదు. ముందుగా మీ ఫోన్‌లో అనుమతిని మంజూరు చేయండి."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ఎమర్జెన్సీ కాల్ లేదా టెక్స్ట్ మెసేజ్ పంపే విషయంలో"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ఎమర్జెన్సీ సర్వీసులకు లొకేషన్ పంపడం జరిగింది"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"ఎమర్జెన్సీ నంబర్‌కు కాల్ చేసినప్పుడు, లేదా టెక్స్ట్ మెసేజ్ పంపినప్పుడు ఈ యాప్ మీ పరికర లొకేషన్‌ను యాక్సెస్ చేసింది. యాప్‌నకు లొకేషన్ అనుమతి లేకున్నా లేదా పరికర లొకేషన్ ఆఫ్‌లో ఉన్నప్పటికీ కూడా ఇలా జరిగవచ్చు. "<a href="https://support.google.com/android/answer/9319337">"మరింత తెలుసుకోండి"</a></string>
</resources>
diff --git a/PermissionController/res/values-th-v34/strings.xml b/PermissionController/res/values-th-v34/strings.xml
index 8e6bae837..e23f92c21 100644
--- a/PermissionController/res/values-th-v34/strings.xml
+++ b/PermissionController/res/values-th-v34/strings.xml
@@ -17,11 +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>
</resources>
diff --git a/PermissionController/res/values-th-watch/strings.xml b/PermissionController/res/values-th-watch/strings.xml
index db9d5dec6..8b6c60c76 100644
--- a/PermissionController/res/values-th-watch/strings.xml
+++ b/PermissionController/res/values-th-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"เปลี่ยนไม่ได้"</string>
<string name="generic_yes" msgid="2489207724988649846">"ใช่"</string>
<string name="generic_cancel" msgid="2631708607129269698">"ยกเลิก"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ตลอดเวลา"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ขณะกำลังใช้แอป"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ตลอดเวลา"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ขณะกำลังใช้แอป"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ตลอดเวลา"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ขณะกำลังใช้แอป"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ตลอดเวลา"</string>
</resources>
diff --git a/PermissionController/res/values-th/strings.xml b/PermissionController/res/values-th/strings.xml
index c6b80ea1f..639f48e91 100644
--- a/PermissionController/res/values-th/strings.xml
+++ b/PermissionController/res/values-th/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"สิทธิ์"</string>
<string name="cancel" msgid="8943320028373963831">"ยกเลิก"</string>
<string name="back" msgid="6249950659061523680">"กลับ"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"ปิด"</string>
<string name="available" msgid="6007778121920339498">"พร้อมใช้งาน"</string>
<string name="blocked" msgid="9195547604866033708">"ถูกบล็อก"</string>
<string name="on" msgid="280241003226755921">"เปิด"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"ไม่สามารถติดตั้ง/ถอนการติดตั้งบน Wear"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"โปรดเลือกข้อมูลที่อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึง"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"อัปเดต &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; แล้ว โปรดเลือกข้อมูลที่อนุญาตให้แอปนี้เข้าถึง"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"ยกเลิก"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"อนุญาตสิทธิ์เข้าถึงแบบจำกัด"</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_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_header_with_device_name" msgid="7193042925656173271">"สิทธิ์เข้าถึง<xliff:g id="PERM">%1$s</xliff:g>สำหรับแอปนี้บน <xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"จัดการแอปหากไม่ได้ใช้งาน"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"นำสิทธิ์ออก ลบไฟล์ชั่วคราว และหยุดการแจ้งเตือน"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"นำสิทธิ์ออก ลบไฟล์ชั่วคราว หยุดการแจ้งเตือน และเก็บแอป"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"หากคุณไม่ได้ใช้งานแอปนาน 2-3 เดือน ระบบจะนำสิทธิ์ของ​แอปนี้ออกเพื่อปกป้องข้อมูลของคุณ"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"หากไม่มีการใช้งานแอปนาน 2-3 เดือน ระบบจะปกป้องข้อมูลของคุณด้วยการนำสิทธิ์ต่อไปนี้ออก ได้แก่ <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"ระบบนำสิทธิ์ออกจากแอปที่คุณไม่ได้ใช้งานนาน 2-3 เดือนเพื่อปกป้องข้อมูลของคุณ"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"ได้รับอนุญาตให้จัดการไฟล์ทั้งหมด"</string>
<string name="ask_header" msgid="2633816846459944376">"ถามทุกครั้ง"</string>
<string name="denied_header" msgid="903209608358177654">"ไม่อนุญาต"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>ใน <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"ดูแอปอื่นๆ ที่สามารถเข้าถึงไฟล์ทั้งหมดได้"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 วัน}other{# วัน}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# ชั่วโมง}other{# ชั่วโมง}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"แอปกระเป๋าเงินเริ่มต้น"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"แอปกระเป๋าเงิน"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"แอปกระเป๋าเงินสามารถจัดเก็บบัตรเครดิต บัตรสะสมคะแนน กุญแจรถยนต์ รวมถึงสิ่งอื่นๆ เพื่อช่วยในการทำธุรกรรมรูปแบบต่างๆ"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"ตั้งค่า <xliff:g id="APP_NAME">%1$s</xliff:g> เป็นแอปกระเป๋าเงินเริ่มต้นไหม"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"ไม่ต้องใช้สิทธิ์"</string>
<string name="request_role_current_default" msgid="738722892438247184">"แอปเริ่มต้นปัจจุบัน"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"ไม่ต้องถามอีก"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"ตั้งเป็นแอปเริ่มต้น"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"แอปเริ่มต้นเพิ่มเติม"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"การเปิดลิงก์"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"ค่าเริ่มต้นสำหรับงาน"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"ค่าเริ่มต้นสำหรับพื้นที่ส่วนตัว"</string>
<string name="default_app_none" msgid="9084592086808194457">"ไม่มี"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(ค่าเริ่มต้นของระบบ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"ไม่มีแอป"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพและสื่อบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรายชื่อติดต่อบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งของ&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งของ&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"เปลี่ยนแปลงสิทธิ์เข้าถึงตำแหน่งของ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ค้นหา เชื่อมต่อ และระบุตำแหน่งโดยสัมพันธ์กับอุปกรณ์ที่อยู่ใกล้เคียงบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"เปลี่ยนแปลงสิทธิ์เข้าถึงตำแหน่งของ <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงตำแหน่งโดยประมาณของ&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงปฏิทินบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ส่งและดูข้อความ SMS บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพ สื่อ และไฟล์บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงเพลงและเสียงบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพและวิดีโอบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงรูปภาพและวิดีโอเพิ่มเติมบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บันทึกเสียงบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"เปลี่ยนแปลงสิทธิ์เข้าถึงไมโครโฟนของ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงกิจกรรมการเคลื่อนไหวร่างกายบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ถ่ายภาพและบันทึกวิดีโอบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ถ่ายภาพและบันทึกวิดีโอบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"เปลี่ยนแปลงสิทธิ์เข้าถึงกล้องของ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงบันทึกการโทรในโทรศัพท์บน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; โทรและจัดการการโทรบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงข้อมูลเซ็นเซอร์เกี่ยวกับสัญญาณชีพบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงข้อมูลเซ็นเซอร์เกี่ยวกับสัญญาณชีพบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;ไหม"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"หากต้องการให้แอปนี้เข้าถึงข้อมูลเซ็นเซอร์ร่างกายได้ทุกเมื่อ แม้ว่าคุณจะไม่ได้ใช้งานแอปอยู่ "<annotation id="link">"ไปที่การตั้งค่า"</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_device_aware_sensors" msgid="5542771499929819675">"อนุญาตให้ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; เข้าถึงข้อมูลเซ็นเซอร์ร่างกายได้ตลอดขณะใช้งานแอปบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"อนุญาตให้&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;ส่งการแจ้งเตือนถึงคุณบน&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"ไม่มี"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 ชั่วโมง\nที่ผ่านมา"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 วัน\nที่ผ่านมา"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> เปอร์เซ็นต์"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> ได้รับการปกป้องโดย Android เนื่องจากมีการประมวลผลข้อมูลในอุปกรณ์ การใช้สิทธิ์ของแอปนี้จะไม่แสดงในแถบสถานะหรือแดชบอร์ดความเป็นส่วนตัว"</string>
<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>
@@ -520,7 +560,14 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"สิทธิ์เข้าถึงกล้องปิดอยู่"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"การเข้าถึงไมโครโฟนปิดอยู่"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"การเข้าถึงตำแหน่งปิดอยู่"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"สำหรับแอปสาระบันเทิง"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"สำหรับแอปที่จำเป็น"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"แอปนี้เป็นแอปที่จำเป็น"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"ผู้ผลิตรถยนต์จำเป็นต้องใช้แอปนี้"</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 +578,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 +628,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"เข้าถึงตอน <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"เข้าถึงเมื่อวานตอน <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"เข้าถึงเมื่อวันที่ <xliff:g id="TIME_DATE_0">%1$s</xliff:g> ตอน <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"รหัสผ่านที่สามารถใช้งานได้เพียงครั้งเดียวของคุณคือ 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"การตั้งค่าที่จำกัด"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"การตั้งค่านี้ใช้ไม่ได้ในตอนนี้เพื่อความปลอดภัยของคุณ"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"แอปถูกปฏิเสธไม่ให้เข้าถึง <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"แอปขอเข้าถึงสิทธิ์ที่มีความละเอียดอ่อนซึ่งอาจทำให้ข้อมูลส่วนบุคคลและข้อมูลทางการเงินของคุณมีความเสี่ยง<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>แอปอาจทำงานได้ไม่ถูกต้องหากไม่มีสิทธิ์ที่จำกัดนี้ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ดูวิธีอนุญาตให้เข้าถึง&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"แอปถูกปฏิเสธไม่ให้เข้าถึงเพื่อเป็น<xliff:g id="ROLE_NAME">%1$s</xliff:g> เริ่มต้น"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"แอปขอเข้าถึงสิทธิ์ที่มีความละเอียดอ่อนซึ่งอาจทำให้ข้อมูลส่วนบุคคลและข้อมูลทางการเงินของคุณมีความเสี่ยง<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>แอปอาจทำงานได้ไม่ถูกต้องหากไม่มีสิทธิ์ที่จำกัดเหล่านี้ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ดูวิธีอนุญาตให้เข้าถึง&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"แอปถูกปฏิเสธไม่ให้เข้าถึง"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"การให้สิทธิ์นี้อาจทำให้ข้อมูลส่วนบุคคลและข้อมูลทางการเงินของคุณมีความเสี่ยง<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>แอปอาจทำงานได้ไม่ถูกต้องหากไม่มีสิทธิ์ที่จำกัดนี้ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;ดูวิธีอนุญาตให้เข้าถึง&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"ดูข้อมูลเพิ่มเติม"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ตกลง"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"ระงับคำขอสิทธิ์อยู่"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"แอปนี้มีการขอสิทธิ์เพิ่มเติม แต่ไม่สามารถให้สิทธิ์ในเซสชันที่กำลังสตรีมอยู่ โปรดให้สิทธิ์ในโทรศัพท์ก่อน"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"สำหรับการโทรหรือส่งข้อความฉุกเฉิน"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ส่งตำแหน่งไปยังบริการช่วยเหลือฉุกเฉินแล้ว"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"แอปนี้เข้าถึงตำแหน่งของอุปกรณ์ระหว่างที่โทรหรือส่งข้อความหาหมายเลขฉุกเฉิน ซึ่งอาจเกิดขึ้นได้แม้ว่าแอปจะไม่มีสิทธิ์เข้าถึงตำแหน่งหรือแม้ว่าตำแหน่งของอุปกรณ์จะปิดอยู่ก็ตาม "<a href="https://support.google.com/android/answer/9319337">"ดูข้อมูลเพิ่มเติม"</a></string>
</resources>
diff --git a/PermissionController/res/values-tl-v34/strings.xml b/PermissionController/res/values-tl-v34/strings.xml
index 4ed588f80..a0b9e58d4 100644
--- a/PermissionController/res/values-tl-v34/strings.xml
+++ b/PermissionController/res/values-tl-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Pamahalaan ang access ng app sa data ng kalusugan"</string>
<string name="location_settings" msgid="8863940440881290182">"Access sa lokasyon"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Para sa mga app at serbisyo. Kung naka-off ang setting na ito, posible pa ring ibahagi ang data ng mikropono kapag tumawag ka sa isang pang-emergency na numero"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Para sa mga app at serbisyo"</string>
</resources>
diff --git a/PermissionController/res/values-tl-watch/strings.xml b/PermissionController/res/values-tl-watch/strings.xml
index 1719c5c5e..33191f04f 100644
--- a/PermissionController/res/values-tl-watch/strings.xml
+++ b/PermissionController/res/values-tl-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Hindi mababago"</string>
<string name="generic_yes" msgid="2489207724988649846">"Oo"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Kanselahin"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Palagi"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Habang ginagamit ang app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Palagi"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Habang ginagamit ang app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Palagi"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Habang ginagamit ang app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Palagi"</string>
</resources>
diff --git a/PermissionController/res/values-tl/strings.xml b/PermissionController/res/values-tl/strings.xml
index d4bdb66e0..7ca0c2852 100644
--- a/PermissionController/res/values-tl/strings.xml
+++ b/PermissionController/res/values-tl/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"mga pahintulot"</string>
<string name="cancel" msgid="8943320028373963831">"Kanselahin"</string>
<string name="back" msgid="6249950659061523680">"Bumalik"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Isara"</string>
<string name="available" msgid="6007778121920339498">"Available"</string>
<string name="blocked" msgid="9195547604866033708">"Naka-block"</string>
<string name="on" msgid="280241003226755921">"Naka-on"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Lahat ng pahintulot"</string>
<string name="other_permissions" msgid="2901186127193849594">"Iba pang kakayahan ng app"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Kahilingan sa pagpapahintulot"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Ang mga pagkilos na I-install/I-uninstall ay hindi sinusuportahan sa Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Piliin kung ano ang papayagang i-access ng &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Na-update na ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Piliin kung ano ang papayagang i-access ng app na ito."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Kanselahin"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Palaging pahintulutan lahat"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Magtanong palagi"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Huwag payagan"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Payagan ang limitadong access"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Eksaktong lokasyon"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Tinatayang lokasyon"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Gamitin ang eksaktong lokasyon"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Kapag naka-off ang eksaktong lokasyon, puwedeng i-access ng mga app ang iyong tinatantyang lokasyon"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Pahintulot sa <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Access sa <xliff:g id="PERM">%1$s</xliff:g> para sa app na ito"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"access sa <xliff:g id="PERM">%1$s</xliff:g> para sa app na ito sa <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Tingnan ang lahat ng pahintulot ng <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Tingnan ang lahat ng app na may ganitong pahintulot"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Ipakita ang paggamit ng mikropono ng assistant"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Alisin ang mga pahintulot kung hindi ginagamit ang app"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Alisin ang pahintulot, magbakante ng espasyo"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"I-pause ang aktibidad sa app kung hindi ginagamit"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Pamahalaan ang app kung hindi ginagamit"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Alisin ang mga pahintulot, i-delete ang mga pansamantalang file, at ihinto ang mga notification"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Alisin ang mga pahintulot, i-delete ang mga pansamantalang file, ihinto ang mga notification, at i-archive ang app"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Para maprotektahan ang iyong data, aalisin ang mga pahintulot para sa app na ito kapag ilang buwan nang hindi ginagamit ang app."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Para maprotektahan ang iyong data, kapag ilang buwan nang hindi ginagamit ang app, aalisin ang mga sumusunod na pahintulot: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Para protektahan ang iyong data, inalis na ang mga pahintulot sa mga app na ilang buwan mo nang hindi ginagamit."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Pinapayagang pamahalaan ang lahat ng file"</string>
<string name="ask_header" msgid="2633816846459944376">"Magtanong palagi"</string>
<string name="denied_header" msgid="903209608358177654">"Hindi pinapayagan"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> noong <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Tumingin pa ng mga app na puwedeng mag-access ng lahat ng file"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 araw}one{# araw}other{# na araw}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# oras}one{# oras}other{# na oras}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notes app"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Mga app na nagbibigay-daan sa iyong magtala sa device mo"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"mga tala"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Default na wallet app"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Wallet app"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Puwedeng i-store ng mga wallet app ang iyong mga credit at loyalty card, susi ng kotse, at iba pang bagay para makatulong sa iba\'t ibang uri ng transaksyon."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Itakda ang <xliff:g id="APP_NAME">%1$s</xliff:g> bilang default mong wallet app?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Walang kailangang pahintulot"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Kasalukuyang default"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Huwag nang itanong muli"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Itakdang default"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Higit pang default"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Pagbubukas ng mga link"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Default para sa trabaho"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Default para sa pribadong space"</string>
<string name="default_app_none" msgid="9084592086808194457">"Wala"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Default ng system)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Walang app"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Ipakita ang pagtukoy ng trigger ng assistant"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Ipakita ang icon sa status bar kapag gumamit ng mikropono para i-activate ang voice assistant"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang mga larawan at media sa iyong device?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang mga larawan at media sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang iyong mga contact?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang iyong mga contact sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang lokasyon ng device na ito?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang lokasyon ng &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Magkakaroon lang ang app ng access sa lokasyon habang ginagamit mo ang app"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang lokasyon ng device na ito?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang lokasyon ng &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Posibleng gustong i-access ng app na ito ang iyong lokasyon sa lahat ng oras, kahit na hindi mo ginagamit ang app. "<annotation id="link">"Payagan sa mga setting."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Baguhin ang access sa lokasyon para sa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Baguhin ang access sa lokasyon para sa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Gustong i-access ng app na ito ang iyong lokasyon sa lahat ng oras, kahit na hindi mo ginagamit ang app. "<annotation id="link">"Payagan sa mga setting."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na maghanap, kumonekta sa, at tukuyin ang relatibong posisyon ng mga kalapit na device?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na humanap, kumonekta, at tumukoy ng relatibong posisyon ng mga kalapit na device sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na hanapin at tukuyin ang, at kumonekta sa relatibong posisyon ng mga kalapit na device? "<annotation id="link">"Payagan sa mga setting."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Gawing eksakto ang access sa lokasyon ng <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> mula sa pagiging tinatantya?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Gawing tumpak mula sa tinataya ang access sa lokasyon ng <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang tinatantyang lokasyon ng device na ito?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang tinatayang lokasyon ng &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Eksakto"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Tinatantya"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang iyong kalendaryo?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang kalendaryo mo sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na magpadala at tumingin ng mga mensaheng SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na magpadala at tingnan ang mga SMS message &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang mga larawan, media, at file sa iyong device?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang mga larawan, media, at file sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang &lt;b&gt;mga larawan, video, musika, at audio&lt;/b&gt; sa device?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang &lt;b&gt;mga larawan, video, musika, audio, at iba pang file&lt;/b&gt; sa device?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang musika at audio sa device na ito?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang musika at audio sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang mga larawan at video sa device na ito?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang mga larawan at video sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang higit pang larawan at video sa device na ito?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-access ng higit pang larawan at video sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Makakapag-record lang ng audio ang app habang ginagamit mo ang app"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na mag-record ng audio sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Posibleng gusto ng app na ito na mag-record ng audio sa lahat ng oras, kahit na hindi mo ginagamit ang app. "<annotation id="link">"Payagan sa mga setting."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"Baguhin ang access sa mikropono para sa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Baguhin ang access sa mikropono para sa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Gusto ng app na ito na mag-record ng audio sa lahat ng oras, kahit na hindi mo ginagamit ang app. "<annotation id="link">"Payagan sa mga setting."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang iyong pisikal na aktibidad?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang iyong pisikal na aktibidad sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na kumuha ng larawan at mag-record ng video?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na kumuha ng mga larawan at mag-record ng video sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Makakakuha lang ng mga larawan at makakapag-precord lang ng video ang app habang ginagamit mo ang app"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na kumuha ng mga larawan at mag-record ng video?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na kumuha ng mga larawan at mag-record ng video sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Posibleng gusto ng app na ito na kumuha ng larawan at mag-record ng video sa lahat ng oras, kahit na hindi mo ginagamit ang app. "<annotation id="link">"Payagan sa settings."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Baguhin ang access sa camera para sa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Baguhin ang access sa camera para sa &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Gusto ng app na ito na kumuha ng mga larawn at mag-record ng video sa lahat ng oras, kahit na hindi mo ginagamit ang app. "<annotation id="link">"Payagan sa mga setting."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang iyong mga log ng tawag sa telepono?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang iyong mga log ng tawag sa telepono sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na tumawag at mamahala ng mga tawag sa telepono?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na gumawa at mamahala ng mga tawag sa telepono sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang data ng sensor tungkol sa iyong mga vital sign?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang data ng sensor tungkol sa mga vital sign mo sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Gusto ng app na ito na palaging i-access ang data ng sensor tungkol sa iyong mga vital sign, kahit hindi mo ginagamit ang app. Para gawin ang pagbabagong ito, "<annotation id="link">"pumunta sa mga setting."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang data ng sensor tungkol sa iyong mga vital sign?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang data ng sensor tungkol sa mga vital sign mo sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Para pahintulutan ang app na ito na i-access ang data ng sensor ng katawan sa lahat ng oras, kahit na hindi mo ginagamit ang app, "<annotation id="link">"pumunta sa mga setting."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Patuloy na pahintulutan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na i-access ang data ng sensor ng katawan habang ginagamit ang app?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Patuloy na payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na ma-access ang data ng sensor ng katawan sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; habang ginagamit ang app?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Payagan ang <xliff:g id="APP_NAME">%1$s</xliff:g> na padalhan ka ng mga notification?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Payagan ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; na magpadala sa iyo ng mga notification sa &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Kontroladong pahintulot"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"May access sa lokasyon ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Pinapayagan ng organisasyon mo ang <xliff:g id="APP_NAME">%1$s</xliff:g> na i-access ang iyong lokasyon"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Wala"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Nakalipas\nna 24 oras"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Nakalipas na\n7 araw"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> (na) porsyento"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Protektado ng Android ang <xliff:g id="APP_NAME">%1$s</xliff:g>. Dahil pinoproseso sa device na ito ang iyong data, hindi ipinapakita sa status bar o sa privacy dashboard mo ang paggamit ng pahintulot ng app na ito."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"Protektado ng Android ang <xliff:g id="APP_NAME">%1$s</xliff:g>. Dahil pinoproseso sa device na ito ang iyong data, hindi ipinapakita sa privacy dashboard mo ang paggamit ng pahintulot ng app na ito."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Naka-block ang camera ng device"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Para sa mga app at serbisyo"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Posible pa ring ibahagi ang data ng mikropono kapag tumawag ka sa isang pang-emergency na numero."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Baguhin"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Naka-off ang access sa camera"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Naka-off ang access sa mikropono"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Naka-off ang access sa lokasyon"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Para sa mga infotainment app"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Para sa mga kinakailangang app"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Kinakailangan ang app na ito"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Kinakailangan ng manufacturer ng iyong sasakyan ang app na ito"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Seguridad at privacy"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"I-scan ang device"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"I-dismiss"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Update sa pagbabahagi ng data"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Binago ng ilang app kung paano posibleng ibahagi ng mga ito ang iyong data ng lokasyon"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Mga Setting"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Na-access noong <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Na-access kahapon nang <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Na-access noong <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Ang iyong pang-isang beses na password ay 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Pinaghihigpitang setting"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Para sa iyong seguridad, hindi available ang setting na ito sa ngayon."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Tinanggihan ang access ng appp sa <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Humiling ang app ng access sa pahintulot sa sensitibong impormasyon na posibleng maglagay ng iyong personal at pinansyal na impormasyon sa panganib.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Posibleng hindi gumana nang maayos ang app kung wala ang pinaghihigpitang pahintulot na ito. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Alamin kung paano payagan ang pag-access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Tinanggihan ang access ng app na maging default na <xliff:g id="ROLE_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Nag-request ang app ng access sa mga pahintulot sa sensitibong impormasyon na posibleng maglagay ng iyong personal at pinansyal na impormasyon sa panganib.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Posibleng hindi gumana nang maayos ang app kung wala ang mga pinaghihigpitang pahintulot na ito. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Alamin kung paano payagan ang pag-access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Tinanggihan ang access ng app"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Puwedeng ilagay sa panganib ng pag-access sa pahintulot na ito ang iyong personal at pinansyal na impormasyon.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Posibleng hindi gumana nang maayos ang app kung wala ang pinaghihigpitang pahintulot na ito. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Alamin kung paano payagan ang pag-access&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Matuto pa"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Pinigilan ang kahilingan sa pahintulot"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Humihingi ang app na ito ng dagdag na pahintulot, pero hindi puwedeng ibigay ang mga pahintulot sa isang session ng streaming. Ibigay muna ang pahintulot sa iyong telepono."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Para sa emergency na tawag o text"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Ipinadala ang lokasyon sa mga serbisyong pang-emergency"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Na-access ng app na ito ang lokasyon ng iyong device habang may tawag o text sa isang pang-emergency na numero. Puwede itong mangyari kahit na walang pahintulot sa lokasyon ang app o naka-off ang lokasyon ng device. "<a href="https://support.google.com/android/answer/9319337">"Matuto pa"</a></string>
</resources>
diff --git a/PermissionController/res/values-tr-v34/strings.xml b/PermissionController/res/values-tr-v34/strings.xml
index 81d533562..95f5539d4 100644
--- a/PermissionController/res/values-tr-v34/strings.xml
+++ b/PermissionController/res/values-tr-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Uygulamaların, sağlık verilerine erişimini yönetin"</string>
<string name="location_settings" msgid="8863940440881290182">"Konum erişimi"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Uygulamalar ve hizmetler için. Bu ayar kapalıyken bir acil durum numarasını aradığınızda mikrofon verileri paylaşılmaya devam edebilir."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Uygulamalar ve hizmetler için"</string>
</resources>
diff --git a/PermissionController/res/values-tr-watch/strings.xml b/PermissionController/res/values-tr-watch/strings.xml
index d4845f76b..bdba397ff 100644
--- a/PermissionController/res/values-tr-watch/strings.xml
+++ b/PermissionController/res/values-tr-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Değiştirilemez"</string>
<string name="generic_yes" msgid="2489207724988649846">"Evet"</string>
<string name="generic_cancel" msgid="2631708607129269698">"İptal"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Her zaman"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Uygulama kullanılırken"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Her zaman"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Uygulama kullanılırken"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Her zaman"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Uygulama kullanılırken"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Her zaman"</string>
</resources>
diff --git a/PermissionController/res/values-tr/strings.xml b/PermissionController/res/values-tr/strings.xml
index 9bda44775..294b7fd98 100644
--- a/PermissionController/res/values-tr/strings.xml
+++ b/PermissionController/res/values-tr/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"izinler"</string>
<string name="cancel" msgid="8943320028373963831">"İptal"</string>
<string name="back" msgid="6249950659061523680">"Geri"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Kapat"</string>
<string name="available" msgid="6007778121920339498">"İzin verildi"</string>
<string name="blocked" msgid="9195547604866033708">"Engellendi"</string>
<string name="on" msgid="280241003226755921">"Açık"</string>
@@ -29,11 +30,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Tüm izinler"</string>
<string name="other_permissions" msgid="2901186127193849594">"Diğer uygulama özellikleri"</string>
<string name="permission_request_title" msgid="8790310151025020126">"İzin isteği"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Yükleme/Yüklemeyi Kaldırma işlemleri Wear\'da desteklenmiyor."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının nelere erişmesine izin vereceğinizi seçin"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; güncellendi. Bu uygulamanın nelere erişmesine izin verileceğini seçin."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"İptal"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Tümüne her zaman izin ver"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Her zaman sor"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"İzin verme"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Sınırlı erişime izin ver"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Tam konum"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Yaklaşık konum"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Tam konumu kullan"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Tam konum kapalıyken uygulamalar, yaklaşık konumunuza erişebilir"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> izni"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Bu uygulamanın <xliff:g id="PERM">%1$s</xliff:g> erişimi için"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> adlı cihazda bu uygulamanın <xliff:g id="PERM">%1$s</xliff:g> erişimi"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Tüm <xliff:g id="APP">%1$s</xliff:g> izinlerini göster"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Bu izne sahip tüm uygulamaları göster"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Yardımcı mikrofon kullanımını göster"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Uygulama kullanılmıyorsa izinleri kaldır"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"İzinleri kaldırıp yer aç"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Kullanılmayan uygulama etkinliğini duraklat"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Kullanılmayan uygulamayı yönet"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"İzinleri kaldır, geçici dosyaları sil ve bildirimleri durdur"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"İzinleri kaldır, geçici dosyaları sil, bildirimleri durdur ve uygulamayı arşivle"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Bu uygulama birkaç ay boyunca kullanılmazsa verilerinizi korumak için uygulamanın izinleri kaldırılır."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Uygulama birkaç ay boyunca kullanılmazsa şu izinler verilerinizi korumak için kaldırılacak: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Birkaç aydır kullanmadığınız uygulamaların izinleri verilerinizi korumak için kaldırıldı."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Tüm dosyaları yönetme izni verilenler"</string>
<string name="ask_header" msgid="2633816846459944376">"Her zaman sorulacaklar"</string>
<string name="denied_header" msgid="903209608358177654">"İzin verilmeyenler"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazında <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Tüm dosyalara erişebilen diğer uygulamaları görün"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 gün}other{# gün}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# saat}other{# saat}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Notlar uygulaması"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Cihazınızda not almanıza olanak tanıyan uygulamalar"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"notlar"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Varsayılan cüzdan uygulaması"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Cüzdan uygulaması"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Cüzdan uygulamaları; kredi ve bağlılık kartlarınızı, araba anahtarlarınızı ve diğer bilgileri depolayarak çeşitli işlem türlerinde size kolaylık sağlayabilir."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g>, varsayılan cüzdan uygulamanız olarak ayarlansın mı?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Herhangi bir izin gerekli değil"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Mevcut varsayılan"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Tekrar sorma"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Varsayılan olarak ayarla"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Diğer varsayılanlar"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Bağlantıları açma"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"İş için varsayılan"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Özel alan için varsayılan"</string>
<string name="default_app_none" msgid="9084592086808194457">"Yok"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Sistem varsayılanı)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Uygulama yok"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Yardımcı uygulama tetikleyici algılamasını göster"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Sesli yardımı etkinleştirmek için mikrofon kullanıldığında simgeyi durum çubuğunda göster"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının cihazınızdaki fotoğraf ve medya içeriğine erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki fotoğraf ve medyalara erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının kişilerinize erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında kişilerinize erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazın konumuna erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazının konumuna erişmesine izin verilsin mi?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Bu uygulama konum bilgisine yalnızca kullanıldığı sırada erişebilecektir"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazın konumuna erişmesine izin verilsin mi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazının konumuna erişmesine izin verilsin mi?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Bu uygulama, kullanmadığınız sırada bile konum bilginize sürekli olarak erişmek isteyebilir. "<annotation id="link">"Ayarlar\'da izin verin."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; için konum adresi değiştirilsin mi?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki konum erişimi değiştirilsin mi?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Bu uygulama, kullanmadığınız sırada bile konum bilginize sürekli olarak erişmek isteyebilir. "<annotation id="link">"Ayarlar\'da izin verin."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına, yakındaki cihazları bulup bağlanma ve göreli konumlarını belirleme izni verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında yakındaki cihazları keşfedip bağlanma ve bu cihazların göreli konumunu belirleme izni verilsin mi?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına, yakındaki cihazları bulup bağlanma ve göreli konumlarını belirleme izni verilsin mi? "<annotation id="link">"Ayarlarda izin ver"</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> uygulamasının konum erişimi, yaklaşık konumdan tam konuma değiştirilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki konum erişimi yaklaşık konum yerine tam konum olarak değiştirilsin mi?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazın yaklaşık konumuna erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazının yaklaşık konumuna erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Tam"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Yaklaşık"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının takviminize erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında takviminize erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının SMS mesajları göndermesine ve görüntülemesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında SMS mesajı gönderip görüntülemesine izin verilsin mi?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının cihazınızdaki fotoğraf, medya ve dosyalara erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki fotoğraf, medya içeriği ve dosyalara erişmesine izin verilsin mi?"</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; için bu cihazdaki &lt;b&gt;fotoğraf, video, müzik ve ses dosyalarına&lt;/b&gt; erişim verilsin mi?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; için cihazdaki &lt;b&gt;fotoğraf, video, müzik, ses vb. dosyalara&lt;/b&gt; erişim verilsin mi?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazda müzik ve ses dosyalarına erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında müzik ve ses dosyalarına erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazdaki fotoğraf ve videolara erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki fotoğraf ve videolara erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının bu cihazdaki diğer fotoğraf ve videolara erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında daha fazla fotoğraf ve videoya erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının ses kaydetmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında ses kaydetmesine izin verilsin mi?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Bu uygulama, yalnızca kullanıldığı sırada ses kaydedebilir"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının ses kaydetmesine izin verilsin mi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında ses kaydetmesine izin verilsin mi?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Bu uygulama, kullanmadığınız sırada bile sürekli olarak ses kaydetmek isteyebilir. "<annotation id="link">"Ayarlar\'da izin verin."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; için mikrofon erişimi değiştirilsin mi?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki mikrofon erişimi değiştirilsin mi?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Bu uygulama, kullanmadığınız sırada bile sürekli olarak ses kaydetmek istiyor. "<annotation id="link">"Ayarlar\'da izin verin."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasına fiziksel aktivitenize erişme izni verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında fiziksel aktivitenize erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının fotoğraf çekmesine ve video kaydı yapmasına izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında resim ve video çekmesine izin verilsin mi?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Bu uygulama, yalnızca kullanıldığı sırada resim çekebilir veya video kaydedebilir"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının resim çekmesine ve video kaydı yapmasına izin verilsin mi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında resim ve video çekmesine izin verilsin mi?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Bu uygulama, kullanmadığınız sırada bile sürekli olarak resim çekmek ve video kaydetmek isteyebilir. "<annotation id="link">"Ayarlar\'da izin verin."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; için kamera erişimi değiştirilsin mi?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki kamera erişimi değiştirilsin mi?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Bu uygulama, kullanmadığınız sırada bile sürekli olarak resim çekmek ve video kaydetmek istiyor. "<annotation id="link">"Ayarlar\'da izin verin."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının telefon arama kayıtlarınıza erişmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında telefonunuzdaki arama kayıtlarına erişmesine izin verilsin mi?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının telefon etmesine ve aramaları yönetmesine izin verilsin mi?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında telefon araması yapıp yönetmesine izin verilsin mi?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&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="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki hayati bulgularla ilgili sensör verilerine erişmesine izin verilsin mi?"</string>
<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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının, &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında hayati bulgularınızla 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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının kullanımdayken &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazındaki vücut sensörü verilerine erişme izni sürdürülsün mü?"</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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uygulamasının size &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; cihazında 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>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Kuruluşunuz <xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasının konumunuza erişmesine izin veriyor"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Yok"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Son\n24 saat"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Son\n7 gün"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> yüzde <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g>, Android korumalıdır. Verileriniz bu cihazda işlendiği için bu uygulamanın izin kullanımı durum çubuğunda veya gizlilik kontrol panelinizde gösterilmez."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g>, Android korumalıdır. Verileriniz bu cihazda işlendiği için bu uygulamanın izin kullanımı gizlilik kontrol panelinizde gösterilmez."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Cihaz kamerası engellenmiş"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Uygulamalar ve hizmetler için"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Mikrofon verileri, bir acil durum numarasını aradığınızda paylaşılmaya devam edilebilir."</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Değiştir"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kamera erişimi kapalı"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofon erişimi kapalı"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Konum erişimi kapalı"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Bilgi-eğlence sistemi uygulamaları için"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Gerekli uygulamalar için"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Bu uygulama gereklidir"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Bu uygulama, araba üreticiniz tarafından gerekli kılınmıştır"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Güvenlik ve gizlilik"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Cihaz taraması yap"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Kapat"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Veri paylaşımı güncellemeleri"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Bazı uygulamalar, konum verilerinizi paylaşma şeklini değiştirdi"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Ayarlar"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Erişim zamanı: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Dün saat <xliff:g id="TIME_DATE">%1$s</xliff:g> itibarıyla erişildi"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Erişim tarihi: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Tek kullanımlık şifreniz 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Kısıtlanmış ayar"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Güvenliğiniz için bu ayar şu anda kullanılamıyor."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Uygulamanın <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> iznine erişimi reddedildi"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Uygulama, kişisel ve finansal bilgilerinizi riske atabilecek hassas bir izne erişim istedi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu kısıtlı izin olmadan uygulama düzgün çalışmayabilir. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Erişime nasıl izin vereceğinizi öğrenin&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Uygulamanın varsayılan <xliff:g id="ROLE_NAME">%1$s</xliff:g> olarak kullanılma erişimi reddedildi"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Uygulama, kişisel ve finansal bilgilerinizi riske atabilecek hassas bilgilere erişim izni istedi.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu kısıtlı izinler olmadan uygulama düzgün çalışmayabilir. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Erişime nasıl izin vereceğinizi öğrenin&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Uygulamanın erişimi reddedildi"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Bu izne erişim, kişisel ve finansal bilgilerinizi riske atabilir.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu kısıtlı izin olmadan uygulama düzgün çalışmayabilir. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Erişime nasıl izin vereceğinizi öğrenin&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Daha fazla bilgi edinin"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"Tamam"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"İzin isteği reddedildi"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Bu uygulama ek izinler istiyor ancak akış oturumundayken izin verilemez. Önce telefonunuzda ilgili izni verin."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Acil durum araması veya mesajı için"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Konum, acil durum hizmetlerine gönderildi"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Bu uygulama, bir acil durum numarasına telefon ederken veya mesaj gönderirken cihazınızın konumuna erişti. Bu durum, uygulamanın konum izni olmadığında veya cihazın konumu kapalı olduğunda bile yaşanabilir ."<a href="https://support.google.com/android/answer/9319337">"Daha fazla bilgi edinin"</a>"."</string>
</resources>
diff --git a/PermissionController/res/values-uk-v34/strings.xml b/PermissionController/res/values-uk-v34/strings.xml
index 6b74a3508..b40b556f4 100644
--- a/PermissionController/res/values-uk-v34/strings.xml
+++ b/PermissionController/res/values-uk-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-uk-watch/strings.xml b/PermissionController/res/values-uk-watch/strings.xml
index 244c24ec8..e83f408ef 100644
--- a/PermissionController/res/values-uk-watch/strings.xml
+++ b/PermissionController/res/values-uk-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Не можна змінити"</string>
<string name="generic_yes" msgid="2489207724988649846">"Так"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Скасувати"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Завжди"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Під час використ. додатка"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Завжди"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Під час використ. додатка"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Завжди"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Під час використ. додатка"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Завжди"</string>
</resources>
diff --git a/PermissionController/res/values-uk/strings.xml b/PermissionController/res/values-uk/strings.xml
index bf4b99d43..e9c83982a 100644
--- a/PermissionController/res/values-uk/strings.xml
+++ b/PermissionController/res/values-uk/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"дозволи"</string>
<string name="cancel" msgid="8943320028373963831">"Скасувати"</string>
<string name="back" msgid="6249950659061523680">"Назад"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Закрити"</string>
<string name="available" msgid="6007778121920339498">"Доступно"</string>
<string name="blocked" msgid="9195547604866033708">"Заблоковано"</string>
<string name="on" msgid="280241003226755921">"Увімкнено"</string>
@@ -32,8 +33,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 +62,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 +73,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Встановлення й видалення не підтримуються на пристроях Android Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Виберіть, до чого додаток &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; матиме доступ"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Додаток &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; оновлено. Виберіть, до чого він матиме доступ."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Скасувати"</string>
@@ -124,7 +125,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>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"Дозволити обмежений доступ"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="PERM">%1$s</xliff:g>: доступ для цього додатка (<xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"Керувати невикористовуваним додатком"</string>
+ <string name="unused_apps_summary" msgid="8839466950318403115">"Вилучити дозволи, видалити тимчасові файли й зупинити сповіщення"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Вилучити дозволи, видалити тимчасові файли, зупинити сповіщення й архівувати додаток"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Щоб захистити ваші дані, дозволи для цього додатка буде автоматично скасовано, якщо ви не будете користуватися ним кілька місяців."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Якщо додаток не використовується впродовж кількох місяців, для захисту ваших даних буде скасовано такі дозволи: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Щоб захистити ваші дані, було скасовано дозволи додатків, які не використовувалися впродовж кількох місяців."</string>
@@ -230,7 +235,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Можуть керувати всіма файлами"</string>
<string name="ask_header" msgid="2633816846459944376">"Запитувати щоразу"</string>
<string name="denied_header" msgid="903209608358177654">"Заборонено"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>)"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Переглянути інші додатки, які мають доступ до всіх файлів"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 день}one{# день}few{# дні}many{# днів}other{# дня}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# година}one{# година}few{# години}many{# годин}other{# години}}"</string>
@@ -350,14 +356,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 +392,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"Гаманець за умовчанням"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Гаманець"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"У гаманцях можуть зберігатися ваші картки постійного покупця й кредитні картки, автомобільні ключі та інші об’єкти, які полегшують здійснення різних трансакцій."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Зробити додаток <xliff:g id="APP_NAME">%1$s</xliff:g> гаманцем за умовчанням?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Дозволи не потрібні"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Поточний за умовчанням"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Не запитувати знову"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Вибрати за умовчанням"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Інші додатки за умовчанням"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Відкривання посилань"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Для роботи за умовчанням"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"За умовчанням для приватного простору"</string>
<string name="default_app_none" msgid="9084592086808194457">"Немає"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(За умовчанням)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Немає додатків"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до фото й медіаконтенту на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до контактів на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до геоданих пристрою &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до геоданих пристрою &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Змінити налаштування доступу до геоданих для додатка &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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">"Дозволити додатку <xliff:g id="APP_NAME">%1$s</xliff:g> знаходити пристрої поблизу, підключатися до них і визначати їх відносне розташування?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; знаходити пристрої поблизу, підключатися до них і визначати їх відносне розташування на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Дозволити додатку <xliff:g id="APP_NAME">%1$s</xliff:g> знаходити пристрої поблизу, підключатися до них і визначати їх відносне розташування? "<annotation id="link">"Дозвольте в налаштуваннях."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Змінити доступ до місцезнаходження для додатка <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> з приблизного на точне?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Змінити доступ до місцезнаходження для додатка <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до приблизного місцезнаходження пристрою &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до календаря на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; надсилати й переглядати SMS на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до фото, медіаконтенту й файлів на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до музики й аудіо на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до фото й відео на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до інших фото й відео на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записувати аудіо на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; записувати аудіо на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Змінити налаштування доступу до мікрофона для додатка &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до ваших даних про фізичну активність на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; робити знімки й записувати відео на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; робити знімки й записувати відео на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Змінити налаштування доступу до камери для додатка &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до журналів викликів на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; здійснювати телефонні дзвінки й керувати ними на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до даних датчиків про ваші життєві показники на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до даних датчиків про ваші життєві показники на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Щоб надати додатку постійний доступ до показників датчиків на тілі, навіть коли він не використовується, "<annotation id="link">"перейдіть у налаштування"</annotation>"."</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Надавати й надалі додатку <xliff:g id="APP_NAME">%1$s</xliff:g> доступ до показників датчиків на тілі, коли він використовується?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Дозволяти й надалі додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; доступ до даних датчиків на тілі на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"Дозволити додатку &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; надсилати вам сповіщення на пристрої &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Немає"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Минулі\n24 години"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Останні\n7 днів"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>: <xliff:g id="PERCENT">%2$d</xliff:g> відсотків"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> захищено системою Android. Оскільки ваші дані обробляються на цьому пристрої, дозволи, які використовує цей додаток, не відображаються на панелі керування дозволами чи в рядку стану."</string>
<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>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"Доступ до камери вимкнено"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Доступ до мікрофона вимкнено"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Доступ до геоданих вимкнено"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Для додатків інформаційно-розважальної системи"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Для обов’язкових додатків"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Це обов’язковий додаток"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Це обов’язковий додаток згідно з вимогами виробника автомобіля"</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>
@@ -581,7 +628,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Доступ отримано: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Доступ отримано вчора: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Доступ отримано: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Ваш одноразовий пароль: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Налаштування з обмеженнями"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"З міркувань безпеки це налаштування наразі недоступне."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Додатку не надано дозвіл \"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>\""</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Додаток запитав дозвіл на доступ до чутливих даних, що може поставити під загрозу вашу особисту й фінансову інформацію.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без цього обмеженого дозволу додаток може не працювати належним чином. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Як надати доступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Додатку не надано доступ до ролі \"<xliff:g id="ROLE_NAME">%1$s</xliff:g>\" за умовчанням"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Додаток запитав дозволи на доступ до чутливих даних, що може поставити під загрозу вашу особисту й фінансову інформацію.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без цих обмежених дозволів додаток може не працювати належним чином. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Як надати доступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Додатку не надано доступ"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Доступ до цього дозволу може поставити під загрозу вашу особисту й фінансову інформацію.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Без цього обмеженого дозволу додаток може не працювати належним чином. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Як надати доступ&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Докладніше"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ОК"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Запит на доступ відхилено"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Цьому додатку потрібно більше дозволів, але їх не можна надати під час потокового передавання. Спершу надайте дозвіл на телефоні."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Для екстрених викликів або SMS"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Геодані надіслано службам екстреної допомоги"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Цей додаток отримав доступ до геоданих вашого пристрою, коли ви телефонували або надсилали SMS на екстрений номер. Це може статися, навіть якщо додаток не має дозволу на доступ до геоданих або коли на пристрої вимкнено геолокацію. "<a href="https://support.google.com/android/answer/9319337">"Докладніше"</a></string>
</resources>
diff --git a/PermissionController/res/values-ur-v34/strings.xml b/PermissionController/res/values-ur-v34/strings.xml
index 37b5ec635..f7aa633cb 100644
--- a/PermissionController/res/values-ur-v34/strings.xml
+++ b/PermissionController/res/values-ur-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-ur-watch/strings.xml b/PermissionController/res/values-ur-watch/strings.xml
index bb0273bc4..1dab4010d 100644
--- a/PermissionController/res/values-ur-watch/strings.xml
+++ b/PermissionController/res/values-ur-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"ناقابل تبدیل"</string>
<string name="generic_yes" msgid="2489207724988649846">"ہاں"</string>
<string name="generic_cancel" msgid="2631708607129269698">"منسوخ کریں"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"ہر وقت"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"ایپ استعمال کرنے کے دوران"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"ہر وقت"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"ایپ استعمال کرنے کے دوران"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"ہر وقت"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"ایپ استعمال کرنے کے دوران"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"ہر وقت"</string>
</resources>
diff --git a/PermissionController/res/values-ur/strings.xml b/PermissionController/res/values-ur/strings.xml
index 62ecf10a9..6dd5978ae 100644
--- a/PermissionController/res/values-ur/strings.xml
+++ b/PermissionController/res/values-ur/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"اجازتیں"</string>
<string name="cancel" msgid="8943320028373963831">"منسوخ کریں"</string>
<string name="back" msgid="6249950659061523680">"واپس جائیں"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"بند کریں"</string>
<string name="available" msgid="6007778121920339498">"دستیاب ہے"</string>
<string name="blocked" msgid="9195547604866033708">"مسدود کردہ"</string>
<string name="on" msgid="280241003226755921">"آن"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"‏\'کارروائیاں انسٹال/اَن انسٹال کریں\' Wear پر تعاون یافتہ نہیں ہے۔"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"‏منتخب کریں کہ &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو کس چیز تک رسائی کی اجازت دینی ہے"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; اپ ڈیٹ ہو گئی ہے۔ منتخب کریں کہ اس ایپ کو کس تک رسائی کی اجازت دینی ہے۔"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"منسوخ کریں"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"محدود رسائی کی اجازت دیں"</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_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_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> پر اس ایپ تک رسائی حاصل کریں<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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"اگر غیر استعمال شدہ ہے تو ایپ کا نظم کریں"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"اجازتیں ہٹائیں، عارضی فائلز حذف کریں اور اطلاعات موقوف کریں"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"اجازتیں ہٹائیں، عارضی فائلوں کو حذف کریں، اطلاعات کو روکیں اور ایپ کو آرکائیو کریں"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"آپ کے ڈیٹا کی حفاظت کے لیے، اگر ایپ کو کچھ مہینوں تک استعمال نہیں کیا گیا تو اس ایپ کے لیے اجازتیں ہٹا دی جائیں گی۔"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"آپ کے ڈیٹا کی حفاظت کے لیے، اگر ایپ کو کچھ مہینوں تک استعمال نہیں کیا گيا تو درج ذیل اجازتیں ہٹا دی جائیں گی: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"آپ کے ڈیٹا کی حفاظت کے لیے، اجازتوں کو ان ایپس سے ہٹا دیا گیا ہے جنہیں آپ نے کچھ مہینوں سے استعمال نہیں کیا ہے۔"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"سبھی فائلز کا نظم کرنے کے ليے اجازت یافتہ"</string>
<string name="ask_header" msgid="2633816846459944376">"ہر بار پوچھیں"</string>
<string name="denied_header" msgid="903209608358177654">"اجازت نہیں ہے"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> پر <xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"مزید ایپس دیکھیں جو تمام فائلز تک رسائی حاصل کر سکتی ہیں"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 دن}other{# دن}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# گھنٹہ}other{# گھنٹے}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"ڈیفالٹ والٹ ایپ"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"والٹ ایپ"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"والٹ ایپس آپ کے کریڈٹ اور لائلٹی کارڈز، کار کی کلیدوں اور دیگر چیزوں کو ٹرانزیکشنز کی مختلف شکلوں میں مدد کے لیے اسٹور کر سکتی ہیں۔"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو آپ کی ڈیفالٹ والٹ ایپ کے طور پر سیٹ کریں؟"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"کوئی اجازت درکار نہیں ہے"</string>
<string name="request_role_current_default" msgid="738722892438247184">"موجودہ ڈیفالٹ"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"دوبارہ نہ پوچھیں"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"بطور ڈیفالٹ سیٹ کریں"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"مزید ڈیفالٹس"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"شروعاتی لنکس"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"کام کیلئے ڈیفالٹ"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"پرائیویٹ اسپیس کے لیے ڈیفالٹ"</string>
<string name="default_app_none" msgid="9084592086808194457">"کوئی نہیں"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(سسٹم ڈیفالٹ)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"کوئی ایپس نہیں ہیں"</string>
@@ -455,48 +467,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر آپ کے رابطوں تک رسائی کے ليے lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اس آلہ کے مقام تک رسائی کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; کے مقام تک رسائی کے ليے &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_device_aware_location" msgid="1264484517831380016">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt; کے مقام تک رسائی کے ليے &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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"‏&lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر &lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کی مقام تک رسائی کو تبدیل کریں؟"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"یہ ایپ ہر وقت آپ کے مقام تک رسائی حاصل کرنا چاہتی ہے، اگرچہ آپ ایپ استعمال نہ کر رہے ہوں۔ "<annotation id="link"></annotation>"ترتیبات میں اجازت دیں۔"</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"‏‎&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;‎ کو قریبی آلات تلاش کرنے، ان سے منسلک ہونے اور ان کی متعلقہ پوزیشن کا تعین کرنے کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"‏&lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر &lt;b&gt;‏<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>&lt;/b&gt; کے مقام تک رسائی کو تخمینی سے قطعی میں تبدیل کریں؟"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اس آلے کے تخمینی مقام تک رسائی کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; کے تخمینی مقام تک رسائی حاصل کرنے کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="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_device_aware_calendar" msgid="7161929851377463612">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر اپنے کیلنڈر تک رسائی حاصل کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو SMS پیغامات بھیجنے اور انہیں ملاحظہ کرنے کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر 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_device_aware_storage" msgid="6933251810928606636">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر تصاویر، میڈیا اور فائلز کی رسائی کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="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_device_aware_read_media_aural" msgid="7927884506238101064">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر مزيد تصاوير اور ویڈیوز تک رسائی حاصل کرنے کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"‏آڈیو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر آڈيو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;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_device_aware_microphone" msgid="3321823187623762958">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر آڈيو ریکارڈ کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&amp;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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"‏&lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر &lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کی مائیکروفون تک رسائی تبدیل کریں؟"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_activityRecognition" msgid="1243869530588745374">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر تصاویر لینے اور ویڈیو ریکارڈ کرنے کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="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_device_aware_camera" msgid="8533353179594971475">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر تصاویر لینے اور ویڈیو ریکارڈ کرنے کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"‏&lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر &lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کی کیمرا تک رسائی تبدیل کریں؟"</string>
<string name="permgroupupgraderequestdetail_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_device_aware_calllog" msgid="8220927190376843309">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر اپنے فون کال لاگز تک رسائی حاصل کرنے کے لیے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديں؟"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"‏&lt;/b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو فون کالز کرنے اور ان کا نظم کرنے کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&amp;gt پر فون کالز کرنے اور ان کا نطم کرنے کے لیے &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_device_aware_sensors" msgid="3874451050573615157">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3687673359121603824">"‏&lt;b&gt;‏<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو آپ کے &lt;b&gt;‏<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر آپ کی علامات حیات کے بارے میں سینسر ڈیٹا تک رسائی کی اجازت دیں؟"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"آپ کے ایپ کا استعمال نہ کرنے کے باوجود اس ایپ کو ہر وقت باڈی سینسر ڈیٹا تک رسائی کی اجازت دینے کے لیے "<annotation id="link">"ترتیبات پر جائیں۔"</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_device_aware_sensors" msgid="5542771499929819675">"‏ايپ استمال ہونے تک &lt;/b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;b&gt; پر باڈی سینسر ڈيٹا کی رسائی کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کو اجازت ديتے رہيں؟"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"‏&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>‏&lt;/b&gt; کو آپ کو اطلاعات بھیجنے کی اجازت دیں؟"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"‏&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; پر آپ کو اطلاعات بھيجنے کے ليے &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; کی اجازت ديں؟"</string>
<string name="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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"کوئی نہیں"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"گزشتہ\n24 گھنٹے"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"گزشتہ\n7 دن"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> فیصد"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"‏‫Android <xliff:g id="APP_NAME">%1$s</xliff:g> کے ساتھ تحفظ یافتہ ہے۔ کیونکہ آپ کے ڈیٹا پر اس آلے پر کارروائی کی جاتی ہے، اس ایپ کی اجازت کا استعمال اسٹیٹس بار یا آپ کے پرائیویسی ڈیش بورڈ پر دکھائی نہیں دیتا ہے۔"</string>
<string name="exempt_info_label" msgid="6286190981253476699">"‏Android <xliff:g id="APP_NAME">%1$s</xliff:g> کے ساتھ تحفظ یافتہ ہے۔ کیونکہ آپ کے ڈیٹا پر اس آلے پر کارروائی کی جاتی ہے، اس ایپ کی اجازت کا استعمال آپ کے پرائیویسی ڈیش بورڈ پر دکھائی نہیں دیتا ہے۔"</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"آلے کا کیمرا مسدود ہے"</string>
@@ -520,6 +560,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"کیمرا تک رسائی آف ہے"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"مائیکروفون تک رسائی آف ہے"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"مقام تک رسائی آف ہے"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"معلوماتی انٹرٹینمنٹ ایپس کے لیے"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"درکار ایپس کیلئے"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"یہ ایپ درکار ہے"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"یہ ایپ آپ کی کار کے مینوفیکچرر کو درکار ہے"</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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g> پر رسائی حاصل کی گئی"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"گزشتہ کل <xliff:g id="TIME_DATE">%1$s</xliff:g> پر رسائی حاصل کی گئی"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g> پر رسائی حاصل کی گئی"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"آپ کا ایک وقتی پاس ورڈ 132435 ہے"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"محدود ترتیب"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"آپ کی سیکیورٹی کیلئے، یہ ترتیب فی الحال دستیاب نہیں ہے۔"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> تک ایپ کی رسائی کو مسترد کر دیا گیا"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"‏ایپ نے ایک حساس اجازت تک رسائی کی درخواست کی ہے جو آپ کی ذاتی اور مالی معلومات کو خطرے میں ڈال سکتی ہے۔<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>یہ ممکن ہے کہ اس محدود اجازت کے بغیر ایپ ٹھیک سے کام نہ کرے۔ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;رسائی کی اجازت دینے کا طریقہ جانیں&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"ڈیفالٹ <xliff:g id="ROLE_NAME">%1$s</xliff:g> ہونے کے لیے ایپ کی رسائی کو مسترد کر دیا گیا"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"‏ایپ نے حساس اجازتوں تک رسائی کی درخواست کی ہے جو آپ کی ذاتی اور مالی معلومات کو خطرے میں ڈال سکتی ہیں۔<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>یہ ممکن ہے کہ ان محدود اجازتوں کے بغیر ایپ ٹھیک سے کام نہ کرے۔ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;رسائی کی اجازت دینے کا طریقہ جانیں&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"ایپ کی رسائی کو مسترد کر دیا گیا"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"‏اس اجازت تک رسائی آپ کی ذاتی اور مالی معلومات کو خطرے میں ڈال سکتی ہے۔<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>یہ ممکن ہے کہ اس محدود اجازت کے بغیر ایپ ٹھیک سے کام نہ کرے۔ &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;رسائی کی اجازت دینے کا طریقہ جانیں&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"مزید جانیں"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"ٹھیک ہے"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"اجازت کی درخواست مسترد کر دی گئی"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"یہ ایپ اضافی اجازتوں کی درخواست کر رہی ہے، لیکن سلسلہ بندی کے سیشن میں اجازتیں نہیں دی جا سکتیں۔ پہلے اپنے فون پر اجازت دیں۔"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"ایمرجنسی کال یا ٹیکسٹ کے لیے"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"ایمرجنسی سروسز کو مقام بھیج دیا گیا"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"اس ایپ نے ایمرجنسی نمبر پر ٹیکسٹ یا کال کے دوران آپ کے آلہ کے مقام تک رسائی حاصل کی۔ ایسا تب بھی ہو سکتا ہے جب ایپ کو مقام کی اجازت نہ ہو یا آلہ کا مقام آف ہو۔ "<a href="https://support.google.com/android/answer/9319337">"مزید جانیں"</a></string>
</resources>
diff --git a/PermissionController/res/values-uz-v34/strings.xml b/PermissionController/res/values-uz-v34/strings.xml
index 3c48cd9d1..21388d616 100644
--- a/PermissionController/res/values-uz-v34/strings.xml
+++ b/PermissionController/res/values-uz-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Salomatlik maʼlumotlariga ilova ruxsatini boshqaring"</string>
<string name="location_settings" msgid="8863940440881290182">"Joylashuv axborotiga ruxsat"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Ilovalar va xizmatlar uchun. Bu sozlama yoqilmasa, favqulodda xizmat raqamiga telefon qilganingizda mikrofon maʼlumotlari hamon ulashilishi mumkin"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Ilovalar va xizmatlar uchun"</string>
</resources>
diff --git a/PermissionController/res/values-uz-watch/strings.xml b/PermissionController/res/values-uz-watch/strings.xml
index dbc7ee1a5..9ac60d9bc 100644
--- a/PermissionController/res/values-uz-watch/strings.xml
+++ b/PermissionController/res/values-uz-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"O‘zgartirilmaydi"</string>
<string name="generic_yes" msgid="2489207724988649846">"Ha"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Bekor q-sh"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Doimo"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Ilova ishlatilayotganda"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Doimo"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Ilova ishlatilayotganda"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Doimo"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Ilova ishlatilayotganda"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Doimo"</string>
</resources>
diff --git a/PermissionController/res/values-uz/strings.xml b/PermissionController/res/values-uz/strings.xml
index 356df948e..a81ee4571 100644
--- a/PermissionController/res/values-uz/strings.xml
+++ b/PermissionController/res/values-uz/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"ruxsatlar"</string>
<string name="cancel" msgid="8943320028373963831">"Bekor qilish"</string>
<string name="back" msgid="6249950659061523680">"Orqaga"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Yopish"</string>
<string name="available" msgid="6007778121920339498">"Mavjud"</string>
<string name="blocked" msgid="9195547604866033708">"Bloklangan"</string>
<string name="on" msgid="280241003226755921">"Yoniq"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Barcha ruxsatlar"</string>
<string name="other_permissions" msgid="2901186127193849594">"Ilovaning boshqa imkoniyatlari"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Ruxsat olish talabi"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear qurilmasi o‘rnatish/o‘chirish amallarini qo‘llab-quvvatlamaydi."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun beriladigan ruxsatlarni tanlang"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; yangilandi. Unga beriladigan ruxsatlarni tanlang."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Bekor qilish"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Doim hammasiga ruxsat"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Har safar soʻralsin"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Rad etish"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Cheklangan ruxsat berish"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Aniq joylashuv"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Taxminiy joylashuv"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Aniq joylashuv axborotidan foydalanish"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Aniq joylashuv yoqilmasa, ilovalar taxminiy joylashuvingiz axborotidan foydalanadi"</string>
<string name="app_permission_title" msgid="2090897901051370711">"<xliff:g id="PERM">%1$s</xliff:g> ruxsati"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Bu ilova uchun <xliff:g id="PERM">%1$s</xliff:g> ruxsati"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>da bu ilova uchun <xliff:g id="PERM">%1$s</xliff:g> kirish ruxsati"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"<xliff:g id="APP">%1$s</xliff:g> uchun berilgan barcha ruxsatlar"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Bu ruxsatga ega barcha ilovalar"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Assistent uchun mikrofondan foydalanishni koʻrsatish"</string>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Ishlatilmayotgan ilovalardan ruxsatlarni olib tashlash"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Ruxsatlarni olib tashlash va joy ochish"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Ishlatilmayotgan ilovalarni pauzalash"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Ishlatilmagan ilovalarni boshqarish"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Ruxsatlarni olib tashlash, vaqtinchalik fayllarni oʻchirish va bildirishnomalarni toʻxtatish"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Ruxsatlarni olib tashlash, vaqtinchalik fayllarni oʻchirish, bildirishnomalarni toʻxtatish va ilovani arxivlash"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Xavfsizlik yuzasidan, bir necha oydan beri ishlatilmagan ilovalardan ruxsatlar olib tashlanadi"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Agar bu ilova bir necha oy ishlatilmasa, quyidagi ruxsatlar olib tashlanadi: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Xavfsizlik yuzasidan, bir necha oydan beri ishlatilmagan ilovalardan ruxsatlar olib tashlanadi"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Barcha fayllarga ruxsati bor"</string>
<string name="ask_header" msgid="2633816846459944376">"Har safar soʻralsin"</string>
<string name="denied_header" msgid="903209608358177654">"Ruxsat berilmagan"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> — <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Barcha fayllarga kirish uchun koʻproq ilovalarni koʻring"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 kun}other{# kun}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# soat}other{# soat}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Qaydlar ilovasi"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Qurilmangizda qaydlar olish imkonini beruvchi ilovalar"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"qaydlar"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Birlamchi hamyon ilovasi"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Hamyon ilovasi"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Hamyon ilovalarda kredit va sodiqlik kartalari, avtomobil kalitlari hamda boshqa raqamli mahsulotlarni saqlash mumkin. Bu har xil turdagi amaliyotlarni bajarishni yanada qulaylashtiradi."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"<xliff:g id="APP_NAME">%1$s</xliff:g> birlamchi hamyon ilovasi sifatida belgilansinmi?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Hech qanday ruxsat zarur emas"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Hozirda asosiy ilova"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Boshqa soʻralmasin"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Birlamchi deb belgilash"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Boshqa asosiy ilovalar"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Havolalarni ochish"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Ish uchun birlamchi"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Maxfiy makon uchun standart"</string>
<string name="default_app_none" msgid="9084592086808194457">"Hech qanday"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Birlamchi)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Hech qanday ilova topilmadi"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Ovozli yordamchi faollashgani haqidagi belgini chiqarish"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Ovozli yordamchi mikrofon orqali faollashtirilganda, bu haqda holat qatorida maxsus belgi chiqadi"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun qurilmadagi suratlar va media fayllarga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida rasmlar va media fayllardan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun kontaktlaringizga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida kontaktlardan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasining joylashuv axborotidan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Bu ilovadan foydalanilayotdangina u joylashuv axborotidan foydalana oladi"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanish ruxsati berilsinmi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasining joylashuv axborotidan foydalanishiga ruxsat berilsinmi?"</string>
<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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasining <xliff:g id="DEVICE_NAME">%2$s</xliff:g> joylashuvidan foydalanish ruxsati 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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida atrofdagi qurilmalarni topishi, ularning nisbiy joylashuvini aniqlashi va ularga ulanishiga 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_device_aware_fineupgrade" msgid="4453775952305587571">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> ilovasining joylashuv axbortiga 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_device_aware_coarselocation" msgid="8367540370912066757">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasining tarxminiy joylashuv axborotidan foydalanishiga ruxsat 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_device_aware_calendar" msgid="7161929851377463612">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida taqvimdan foydalanishiga ruxsat 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_device_aware_sms" msgid="6639977653040502291">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida SMS xabarlarni oʻqishi va yuborishiga 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_device_aware_storage" msgid="6933251810928606636">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida rasmlar, media va boshqa fayllardan foydalanishiga ruxsat 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>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi surat, video, musiqa, audio va fayllarga kirish ruxsati berilsinmi?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun ushbu qurilmadagi musiqa va audio fayllarga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida musiqa va audio fayllaridan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun qurilmadagi suratlar va videolarga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida rasm va videolardan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun qurilmadagi boshqa surat va videolarga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida koʻproq rasm va videolardan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun audio yozib olish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida audio yozib olishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Bu ilova faqat undan foydalanganingizda ovozlarni yozib oladi"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun audio yozib olishga ruxsat berilsinmi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida audio yozib olishiga ruxsat berilsinmi?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Bu ilova doimo, hatto undan foydalanmagan vaqtlaringizda ham ovoz yozib olishi mumkin. "<annotation id="link">"Sozlamalar orqali ruxsat bering."</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun mikrofonga ruxsat oʻzgartirilsinmi?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasining <xliff:g id="DEVICE_NAME">%2$s</xliff:g> mikrofonidan foydalanish ruxsati oʻzgartirilsinmi?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"Bu ilova doimo, hatto undan foydalanmagan vaqtlaringizda ham ovoz yozib olishi mumkin. "<annotation id="link">"Sozlamalar orqali ruxsat bering."</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun jismoniy harakatlaringizga oid axborotga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida jismoniy harakatlar tarixidan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun surat va videoga olish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida rasm va video yozib olishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"Bu ilova faqat undan foydalanganingizda rasm va videoga oladi"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun surat va videoga olishga ruxsat berilsinmi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida rasm va video yozib olishiga ruxsat berilsinmi?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Bu ilova doimo, hatto undan foydalanmagan vaqtlaringizda ham rasm va videoga olishi mumkin. "<annotation id="link">"Sozlamalar orqali ruxsat bering."</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun kameraga ruxsat oʻzgartirilsinmi?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasining <xliff:g id="DEVICE_NAME">%2$s</xliff:g> kamerasidan foydalanish ruxsati oʻzgartirilsinmi?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Bu ilova doimo, hatto undan foydalanmagan vaqtlaringizda ham rasm va videoga olishi mumkin. "<annotation id="link">"Sozlamalar orqali ruxsat bering."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun telefoningizdagi chaqiruvlar tarixiga kirish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida telefon chaqiruvlari tarixidan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun telefon chaqiruvlarini amalga oshirish va boshqarish ruxsati berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida telefon chaqiruvlarini amalga oshirishi va ularni boshqarishiga ruxsat berilsinmi?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; uchun tana holati haqidagi sensor axborotlariga ruxsat berilsinmi?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida organizm holati haqidagi sensor maʼlumotlaridan foydalanishiga ruxsat berilsinmi?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Bu ilova organizm holati haqidagi sensor maʼlumotlaringizdan doimiy foydalanishga ruxsat olmoqchi. Bu ruxsatni berish uchun "<annotation id="link">"sozlamalarni oching."</annotation></string>
<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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida organizm holati haqidagi sensor maʼlumotlaridan foydalanishiga 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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasining <xliff:g id="DEVICE_NAME">%2$s</xliff:g> tana sezgilari axborotidan foydalanishga ruxsati saqlanib qolsinmi?"</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="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmasida sizga bildirishnoma yuborishiga 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>
@@ -512,14 +551,22 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Hech qanday"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Oxirgi\n24 soat"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Oxirgi\n7 kun"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> foiz"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<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="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>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Kameradan foydalanish taqiqlangan"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Mikrofonga ruxsat oʻchiq"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Joylashuvga ruxsat oʻchiq"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Axborot-hordiq ilovalari uchun"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Zaruriy ilovalar uchun"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Bu ilova zaruriy"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Avtomobil ishlab chiqaruvchisi bu ilovani talab qiladi"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Xavfsizlik va maxfiylik"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Qurilmani tekshirish"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Yopish"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Axborot ulashuvi yangilanishi"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Ayrim ilovalarda joylashuv axboroti ulashuvi oʻzgardi"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Sozlamalar"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Kirilgan: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Kecha kirilgan: <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Kirilgan: <xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Bir martalik parolingiz: 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Cheklangan sozlama"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Xavfsizlik maqsadida bu sozlama hozir ishlamaydi."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Ilovaga <xliff:g id="PERMISSION_NAME">%1$s</xliff:g> ruxsati berilmadi"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Ilova maxfiy maʼlumotlarga kirish uchun ruxsat soʻramoqda. Ruxsat bersangiz, shaxsiy va moliyaviy maʼlumotlaringiz xavf ostida qolishi mumkin.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g> Bu cheklangan ruxsatsiz ilova toʻgʻri ishlamasligi mumkin. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Qanday ruxsat berish haqida batafsil&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Ilovadan <xliff:g id="ROLE_NAME">%1$s</xliff:g> turkumidagi standart sifatida foydalanish taqiqlangan"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Ilova maxfiy maʼlumotlarga kirish uchun ruxsat soʻramoqda. Ruxsat bersangiz, shaxsiy va moliyaviy maʼlumotlaringiz xavf ostida qolishi mumkin.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g> Bu cheklangan ruxsatlarsiz ilova toʻgʻri ishlamasligi mumkin. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Qanday ruxsat berish haqida batafsil&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Ilovaga ruxsat rad etildi"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Bu ruxsatdan foydalanish shaxsiy va moliyaviy axborotlaringizni xavf ostiga qoʻyishi mumkin.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Bu cheklangan ruxsatsiz ilova toʻgʻri ishlamasligi mumkin. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Qanday ruxsat berish haqida batafsil&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Batafsil"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Ruxsat talabi bloklandi"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Bu ilova qoʻshimcha ruxsatlar talab qilmoqda, lekin ruxsatlar striming seansida berilmaydi. Avval telefoningizda ruxsat bering."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Favqulodda chaqiruv yoki SMS uchun"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Joylashuv axboroti favqulodda xizmatlarga yuborildi"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Bu ilova favqulodda xizmat raqamini chaqirish yoki SMS yuborish vaqtida qurilmangiz joylashuv axborotidan foydalandi. Bu ilovaga joylashuv axboroti uchun ruxsat berilmagan yoki qurilmada joylashuv xizmati faolsizlantirilgan boʻlsa ham yuz berishi mumkin. "<a href="https://support.google.com/android/answer/9319337">"Batafsil"</a></string>
</resources>
diff --git a/PermissionController/res/values-v31/styles.xml b/PermissionController/res/values-v31/styles.xml
index a05fd488b..78c6f75f8 100644
--- a/PermissionController/res/values-v31/styles.xml
+++ b/PermissionController/res/values-v31/styles.xml
@@ -44,7 +44,8 @@
<style name="PermissionGrantButtonTop"
parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">56dp</item>
+ <item name="android:minHeight">56dp</item>
+ <item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginLeft">24dp</item>
<item name="android:layout_marginRight">24dp</item>
<item name="android:layout_marginTop">2dp</item>
@@ -58,7 +59,8 @@
<style name="PermissionGrantButtonMiddle"
parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">56dp</item>
+ <item name="android:minHeight">56dp</item>
+ <item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginLeft">24dp</item>
<item name="android:layout_marginRight">24dp</item>
<item name="android:layout_marginTop">2dp</item>
@@ -72,7 +74,8 @@
<style name="PermissionGrantButtonBottom"
parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">56dp</item>
+ <item name="android:minHeight">56dp</item>
+ <item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginLeft">24dp</item>
<item name="android:layout_marginRight">24dp</item>
<item name="android:layout_marginTop">2dp</item>
@@ -90,23 +93,24 @@
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:minHeight">48dp</item>
<item name="android:layout_marginTop">16dp</item>
<item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
<item name="android:paddingTop">8dp</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..a63586591 100644
--- a/PermissionController/res/values-v34/strings.xml
+++ b/PermissionController/res/values-v34/strings.xml
@@ -28,8 +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>
</resources>
diff --git a/PermissionController/res/values-v34/styles.xml b/PermissionController/res/values-v34/styles.xml
index 53780ce76..e6cc4ba79 100644
--- a/PermissionController/res/values-v34/styles.xml
+++ b/PermissionController/res/values-v34/styles.xml
@@ -123,6 +123,7 @@
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
<item name="android:lineSpacingMultiplier">1.25</item>
+ <item name="android:hyphenationFrequency">normalFast</item>
</style>
<style name="PermissionRationaleSectionPurposeList"
@@ -370,5 +371,4 @@
</style>
<!-- END SAFETY CENTER SUBPAGE -->
-
</resources> \ No newline at end of file
diff --git a/PermissionController/res/values-v35/bools.xml b/PermissionController/res/values-v35/bools.xml
new file mode 100644
index 000000000..f015b636e
--- /dev/null
+++ b/PermissionController/res/values-v35/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright 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.
+-->
+
+<resources>
+ <bool name="is_at_least_v">true</bool>
+</resources>
diff --git a/PermissionController/res/values-v35/themes.xml b/PermissionController/res/values-v35/themes.xml
new file mode 100644
index 000000000..49da01c6c
--- /dev/null
+++ b/PermissionController/res/values-v35/themes.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+
+
+ <style name="CarSettings" parent="Theme.CarUi.WithToolbar">
+ <item name="carDividerColor">@color/car_divider_color</item>
+ <!--TODO b/331250994, Remove this once we have a strategy to support edgeToEdge -->
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <!--
+ TODO(b/309578419): Make activities handle insets properly and then remove this.
+ -->
+ <style name="OptOutEdgeToEdgeEnforcement">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+</resources>
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-v34/strings.xml b/PermissionController/res/values-vi-v34/strings.xml
index 618a266d8..836d98f28 100644
--- a/PermissionController/res/values-vi-v34/strings.xml
+++ b/PermissionController/res/values-vi-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Quản lý quyền truy cập của ứng dụng vào dữ liệu sức khoẻ"</string>
<string name="location_settings" msgid="8863940440881290182">"Quyền truy cập thông tin vị trí"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Áp dụng cho các ứng dụng và dịch vụ. Nếu bạn tắt chế độ cài đặt này, 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>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Áp dụng cho các ứng dụng và dịch vụ"</string>
</resources>
diff --git a/PermissionController/res/values-vi-watch/strings.xml b/PermissionController/res/values-vi-watch/strings.xml
index 21054e6d4..21ff1bd5c 100644
--- a/PermissionController/res/values-vi-watch/strings.xml
+++ b/PermissionController/res/values-vi-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Không thể thay đổi"</string>
<string name="generic_yes" msgid="2489207724988649846">"Có"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Hủy"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Mọi lúc"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Khi dùng ứng dụng"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Mọi lúc"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Khi dùng ứng dụng"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Mọi lúc"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Khi dùng ứng dụng"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Mọi lúc"</string>
</resources>
diff --git a/PermissionController/res/values-vi/strings.xml b/PermissionController/res/values-vi/strings.xml
index 2f62d1637..ada13f53e 100644
--- a/PermissionController/res/values-vi/strings.xml
+++ b/PermissionController/res/values-vi/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"quyền"</string>
<string name="cancel" msgid="8943320028373963831">"Hủy"</string>
<string name="back" msgid="6249950659061523680">"Quay lại"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Đóng"</string>
<string name="available" msgid="6007778121920339498">"Được phép"</string>
<string name="blocked" msgid="9195547604866033708">"Bị chặn"</string>
<string name="on" msgid="280241003226755921">"Đang bật"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Tất cả các quyền"</string>
<string name="other_permissions" msgid="2901186127193849594">"Các khả năng khác của ứng dụng"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Yêu cầu quyền"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Các hành động Cài đặt/Gỡ cài đặt không được hỗ trợ trên Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Chọn nội dung &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; được phép truy cập vào"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"Đã cập nhật &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;. Chọn nội dung ứng dụng này được phép truy cập vào."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Hủy"</string>
@@ -191,20 +192,24 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Luôn cho phép tất cả"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Luôn hỏi"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Không cho phép"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Cho phép quyền truy cập bị hạn chế"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Vị trí chính xác"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Vị trí ước chừng"</string>
<string name="app_permission_location_accuracy" msgid="7166912915040018669">"Sử dụng vị trí chính xác"</string>
<string name="app_permission_location_accuracy_subtitle" msgid="2654077606404987210">"Khi chế độ vị trí chính xác đang tắt, các ứng dụng có thể truy cập vào thông tin vị trí gần đúng của bạn"</string>
<string name="app_permission_title" msgid="2090897901051370711">"Quyền <xliff:g id="PERM">%1$s</xliff:g>"</string>
<string name="app_permission_header" msgid="2951363137032603806">"Quyền truy cập <xliff:g id="PERM">%1$s</xliff:g> cho ứng dụng này"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Quyền truy cập <xliff:g id="PERM">%1$s</xliff:g> cho ứng dụng này trên <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="app_permission_footer_app_permissions_link" msgid="4926890342636587393">"Xem tất cả các quyền của <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="app_permission_footer_permission_apps_link" msgid="3941988129992794327">"Xem tất cả ứng dụng có quyền này"</string>
<string name="assistant_mic_label" msgid="1011432357152323896">"Hiển thị việc sử dụng micrô của Trợ lý"</string>
<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_label_v3" msgid="693340578642156657">"Quản lý ứ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="unused_apps_summary_v2" msgid="5011313200815115802">"Loại bỏ quyền, xoá tệp tạm thời, dừng thông báo và lưu trữ ứng dụng"</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>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Để bảo vệ dữ liệu của bạn, quản trị viên CNTT đã thu hồi các quyền đối với những ứng dụng bạn không dùng trong vài tháng."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Được phép quản lý tất cả các tệp"</string>
<string name="ask_header" msgid="2633816846459944376">"Luôn hỏi"</string>
<string name="denied_header" msgid="903209608358177654">"Không được phép"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> trên <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Xem thêm ứng dụng có thể truy cập tất cả các tệp"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 ngày}other{# ngày}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# giờ}other{# giờ}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"Ứng dụng ghi chú"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Các ứng dụng cho phép bạn tạo ghi chú trên thiết bị"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"ghi chú"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"Ứng dụng ví mặc định"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"Ứng dụng ví"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Ứng dụng ví có thể lưu trữ thông tin thẻ tín dụng và thẻ khách hàng thân thiết của bạn, chìa khoá ô tô của bạn, cùng nhiều thông tin khác giúp bạn thực hiện nhiều hình thức giao dịch đa dạng."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Đặt <xliff:g id="APP_NAME">%1$s</xliff:g> làm ứng dụng ví mặc định?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Không cần quyền"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Ứng dụng mặc định hiện tại"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Không hỏi lại"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Đặt làm mặc định"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Ứng dụng mặc định khác"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Mở đường liên kết"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Ứng dụng mặc định cho công việc"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Ứng dụng mặc định cho không gian riêng tư"</string>
<string name="default_app_none" msgid="9084592086808194457">"Không có"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Ứng dụng mặc định của hệ thống)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Không có ứng dụng nào"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Hiển thị tính năng phát hiện trình kích hoạt Trợ lý"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Hiển thị biểu tượng trong thanh trạng thái khi sử dụng micrô để kích hoạt trợ lý thoại"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh và nội dung nghe nhìn trên thiết bị?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh và nội dung nghe nhìn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào danh bạ của bạn?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào danh bạ của bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào thông tin vị trí của thiết bị này?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"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 vị trí của &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Ứng dụng này sẽ chỉ có quyền truy cập vào vị trí khi bạn đang sử dụng ứng dụng này"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào thông tin vị trí của thiết bị này?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"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 vị trí của &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Ứng dụng này có thể muốn truy cập vào thông tin vị trí của bạn mọi lúc, ngay cả khi bạn không dùng ứng dụng. "<annotation id="link">"Hãy cho phép trong phần cài đặt."</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Thay đổi quyền truy cập vào thông tin vị trí đối với &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Thay đổi quyền truy cập vào dữ liệu vị trí của &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Ứng dụng này muốn truy cập vào thông tin vị trí của bạn mọi lúc, ngay cả khi bạn không dùng ứng dụng. "<annotation id="link">"Hãy cho phép trong phần cài đặt."</annotation></string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tìm, kết nối và xác định vị trí tương đối của các thiết bị ở gần?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tìm, kết nối và xác định vị trí tương đối của các thiết bị ở gần trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; tìm, kết nối và xác định vị trí tương đối của các thiết bị ở gần? "<annotation id="link">"Bạn có thể cho phép việc này trong phần cài đặt."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Thay đổi quyền truy cập thông tin vị trí của <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> từ vị trí gần đúng thành vị trí chính xác?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Thay đổi quyền truy cập dữ liệu vị trí của <xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; từ ước chừng thành chính xác?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào thông tin vị trí gần đúng của thiết bị này?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào thông tin vị trí ước chừng của &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Chính xác"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Gần đúng"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào lịch của bạn?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào lịch của bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gửi và xem tin nhắn SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gửi và xem tin nhắn SMS trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh, nội dung nghe nhìn và tệp trên thiết bị của bạn?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh, nội dung nghe nhìn và tệp trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào &lt;b&gt;ảnh, video, nhạc và âm thanh&lt;/b&gt; trên thiết bị này?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào &lt;b&gt;ảnh, video, nhạc, âm thanh và các tệp khác&lt;/b&gt; trên thiết bị này?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào nhạc và âm thanh trên thiết bị này?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào nhạc và âm thanh trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh và video trên thiết bị này?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh và video trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào ảnh và video khác trên thiết bị này?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào thêm nhiều ảnh và video trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Ứng dụng này chỉ có thể ghi âm khi bạn đang dùng ứng dụng"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ghi âm trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Thay đổi quyền truy cập vào micrô của &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"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 về hoạt động thể chất của bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chụp ảnh và quay video trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chụp ảnh và quay video trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"Ứng dụng này có thể muốn chụp ảnh và quay video 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_camera" msgid="640758449200241582">"Thay đổi quyền sử dụng máy ảnh của &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Thay đổi quyền truy cập vào camera của &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Ứng dụng này muốn chụp ảnh và quay video 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_calllog" msgid="2065327180175371397">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào nhật ký cuộc gọi điện thoại của bạn?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; truy cập vào nhật ký cuộc gọi của bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; thực hiện và quản lý cuộc gọi điện thoại?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; thực hiện và quản lý cuộc gọi điện thoại trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"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 cảm biến về các dấu hiệu sinh tồn của bạn?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"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 cảm biến về dấu hiệu sinh tồn của bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Ứng dụng này muốn truy cập vào dữ liệu cảm biến về các dấu hiệu sinh tồn của bạn bất cứ lúc nào, ngay cả khi bạn không dùng ứng dụng. Để thay đổi, hãy "<annotation id="link">"chuyển đến phần cài đặt."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"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 cảm biến về các dấu hiệu sinh tồn của bạn?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"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 cảm biến về dấu hiệu sinh tồn của bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Để cho phép ứng dụng này truy cập vào dữ liệu cảm biến cơ thể bất cứ lúc nào, ngay cả khi bạn đang không dùng ứng dụng, "<annotation id="link">"chuyển đến phần cài đặt."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Luôn 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 cảm biến cơ thể trong khi đang dùng ứng dụng?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Vẫn 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 cảm biến cơ thể trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; trong khi đang dùng ứng dụng?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gửi thông báo cho bạn?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Cho phép &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; gửi thông báo cho bạn trên &lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Các quyền bị kiểm soát"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"<xliff:g id="APP_NAME">%1$s</xliff:g> có quyền truy cập thông tin vị trí"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Tổ chức của bạn cho phép <xliff:g id="APP_NAME">%1$s</xliff:g> truy cập thông tin vị trí của bạn"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Không có"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"24 giờ\nqua"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"7 ngày\nqua"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> <xliff:g id="PERCENT">%2$d</xliff:g> phần trăm"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> được Android bảo vệ. Vì dữ liệu của bạn được xử lý trên thiết bị này nên hoạt động sử dụng quyền của ứng dụng này không hiện trên thanh trạng thái hoặc bảng tổng quan về quyền riêng tư."</string>
<string name="exempt_info_label" msgid="6286190981253476699">"<xliff:g id="APP_NAME">%1$s</xliff:g> được Android bảo vệ. Vì dữ liệu của bạn được xử lý trên thiết bị này nên hoạt động sử dụng quyền của ứng dụng này không hiện trên bảng tổng quan về quyền riêng tư."</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Quyền truy cập vào máy ảnh của thiết bị đã bị chặn"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Áp dụng cho các ứng dụng và dịch vụ"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"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>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Thay đổi"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Quyền truy cập vào camera đang tắt"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Tính năng truy cập micrô đang tắt"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Tính năng truy cập thông tin vị trí đang tắt"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Đối với các ứng dụng thông tin giải trí"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Đối với các ứng dụng bắt buộc"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Cần có ứng dụng này"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Nhà sản xuất ô tô của bạn yêu cầu ứng dụng này"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Bảo mật và quyền riêng tư"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Quét thiết bị"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Đóng"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Nội dung cập nhật về cách thức chia sẻ dữ liệu"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Một số ứng dụng đã thay đổi cách thức có thể được dùng để chia sẻ dữ liệu vị trí của bạn"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Cài đặt"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Truy cập lúc <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Truy cập hôm qua lúc <xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Truy cập lúc <xliff:g id="TIME_DATE_1">%2$s</xliff:g> ngày <xliff:g id="TIME_DATE_0">%1$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Mật khẩu một lần của bạn là 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Chế độ cài đặt bị hạn chế"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Để đảm bảo an toàn cho bạn, chế độ cài đặt này hiện không dùng được."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"Ứng dụng đã bị từ chối cấp quyền truy cập vào <xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"Ứng dụng này yêu cầu quyền truy cập vào thông tin nhạy cảm. Việc đó có thể khiến thông tin cá nhân và thông tin tài chính của bạn gặp rủi ro.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Có thể ứng dụng sẽ hoạt động không đúng cách nếu không được cấp quyền bị hạn chế này. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Tìm hiểu cách cấp quyền&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"Ứng dụng đã bị từ chối cấp quyền để làm <xliff:g id="ROLE_NAME">%1$s</xliff:g> mặc định"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"Ứng dụng này yêu cầu quyền truy cập vào thông tin nhạy cảm. Việc đó có thể khiến thông tin cá nhân và thông tin tài chính của bạn gặp rủi ro.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Có thể ứng dụng sẽ hoạt động không đúng cách nếu không được cấp quyền bị hạn chế này. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Tìm hiểu cách cấp quyền&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"Ứng dụng đã bị từ chối cấp quyền"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Việc cấp quyền này có thể khiến thông tin cá nhân và thông tin tài chính của bạn gặp rủi ro.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Có thể ứng dụng sẽ hoạt động không đúng cách nếu không được cấp quyền bị hạn chế này. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Tìm hiểu cách cấp quyền&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Tìm hiểu thêm"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"OK"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Yêu cầu quyền đã bị chặn"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Ứng dụng này đang yêu cầu thêm quyền, nhưng bạn không thể cấp quyền trong một phiên truyền trực tuyến. Trước tiên, hãy cấp quyền trên điện thoại của bạn."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Đối với cuộc gọi hoặc tin nhắn khẩn cấp"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Thông tin vị trí được gửi đến dịch vụ khẩn cấp"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Ứng dụng này đã truy cập thông tin vị trí thiết bị của bạn trong khi gọi điện hoặc nhắn tin tới một số khẩn cấp. Điều này có thể xảy ra ngay cả khi ứng dụng này không có quyền truy cập thông tin vị trí hoặc dịch vụ vị trí trên thiết bị đang tắt. "<a href="https://support.google.com/android/answer/9319337">"Tìm hiểu thêm"</a></string>
</resources>
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-watch/donottranslate.xml b/PermissionController/res/values-watch/donottranslate.xml
new file mode 100644
index 000000000..c3ab3cbb1
--- /dev/null
+++ b/PermissionController/res/values-watch/donottranslate.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- font-family-device-default is expected to be preloaded in the font_customization.xml(/vendor/<OEM>/products/<PRODUCT>/fonts/fonts_customization.xml)-->
+ <!-- Falls back to system default when font-family-device-default doesn't exist -->
+ <string name="wear_material_compose_display_1_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_display_2_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_display_3_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_title_1_font_family">font-family-medium-device-default</string>
+ <string name="wear_material_compose_title_2_font_family">font-family-medium-device-default</string>
+ <string name="wear_material_compose_title_3_font_family">font-family-device-default</string>
+ <string name="wear_material_compose_body_1_font_family">font-family-text-device-default</string>
+ <string name="wear_material_compose_body_2_font_family">font-family-text-device-default</string>
+ <string name="wear_material_compose_button_font_family">font-family-text-medium-device-default</string>
+ <string name="wear_material_compose_caption_1_font_family">font-family-text-medium-device-default</string>
+ <string name="wear_material_compose_caption_2_font_family">font-family-text-medium-device-default</string>
+ <string name="wear_material_compose_caption_3_font_family">font-family-text-medium-device-default</string>
+</resources>
diff --git a/PermissionController/res/values-watch/strings.xml b/PermissionController/res/values-watch/strings.xml
index f940eae65..60aca8fe9 100644
--- a/PermissionController/res/values-watch/strings.xml
+++ b/PermissionController/res/values-watch/strings.xml
@@ -35,4 +35,25 @@
<!-- Generic text to indicate Cancel. [CHAR LIMIT=10] -->
<string name="generic_cancel">Cancel</string>
+
+ <!-- [CHAR LIMIT=25] App can always (when app is in foreground or background) access the resource protected by the permission -->
+ <string name="permission_access_always">All the time</string>
+
+ <!-- App can only access the resource protected by the permission while app is in foreground [CHAR LIMIT=25]-->
+ <string name="permission_access_only_foreground">While using app</string>
+
+ <!-- Title for the dialog button to allow a permission grant when you can also only allow in the foreground. [CHAR LIMIT=25] -->
+ <string name="app_permission_button_allow_always">All the time</string>
+
+ <!-- Title for the dialog button to allow a permission grant only when the app is in the foreground. [CHAR LIMIT=25] -->
+ <string name="app_permission_button_allow_foreground">While using app</string>
+
+ <!-- Title for the dialog button to allow a permission grant when you can also only allow in the foreground. [CHAR LIMIT=25] -->
+ <string name="grant_dialog_button_allow_always">All the time</string>
+
+ <!-- Title for the dialog button to allow a permission grant only when the app is in the foreground. [CHAR LIMIT=25] -->
+ <string name="grant_dialog_button_allow_foreground">While using app</string>
+
+ <!-- Title for the dialog button to allow a change from foreground to background permission grant. [CHAR LIMIT=25] -->
+ <string name="grant_dialog_button_allow_background">All the time</string>
</resources>
diff --git a/PermissionController/res/values-watch/themes.xml b/PermissionController/res/values-watch/themes.xml
index 0e9b7a919..822bb55d1 100644
--- a/PermissionController/res/values-watch/themes.xml
+++ b/PermissionController/res/values-watch/themes.xml
@@ -21,6 +21,10 @@
<style name="Theme.PermissionController.Settings" parent="Settings" />
<style name="GrantPermissions" parent="@android:style/Theme.DeviceDefault.NoActionBar">
- <item name="android:windowBackground">@android:color/transparent</item>
</style>
+
+ <style name="RequestRole" parent="Settings" />
+
+ <style name="Theme.PermissionController.IncidentReportDialog"
+ parent="@android:style/Theme.DeviceDefault.Dialog.Alert" />
</resources>
diff --git a/PermissionController/res/values-zh-rCN-v34/strings.xml b/PermissionController/res/values-zh-rCN-v34/strings.xml
index 7bebdf9bc..fbb8bc6db 100644
--- a/PermissionController/res/values-zh-rCN-v34/strings.xml
+++ b/PermissionController/res/values-zh-rCN-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-zh-rCN-watch/strings.xml b/PermissionController/res/values-zh-rCN-watch/strings.xml
index 67671132f..b8860dfbb 100644
--- a/PermissionController/res/values-zh-rCN-watch/strings.xml
+++ b/PermissionController/res/values-zh-rCN-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"无法更改"</string>
<string name="generic_yes" msgid="2489207724988649846">"是"</string>
<string name="generic_cancel" msgid="2631708607129269698">"取消"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"始终"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"在使用应用时"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"始终"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"在使用应用时"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"始终"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"在使用应用时"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"始终"</string>
</resources>
diff --git a/PermissionController/res/values-zh-rCN/strings.xml b/PermissionController/res/values-zh-rCN/strings.xml
index bde1c8f89..031937f03 100644
--- a/PermissionController/res/values-zh-rCN/strings.xml
+++ b/PermissionController/res/values-zh-rCN/strings.xml
@@ -21,6 +21,8 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"权限"</string>
<string name="cancel" msgid="8943320028373963831">"取消"</string>
<string name="back" msgid="6249950659061523680">"返回"</string>
+ <!-- no translation found for dialog_close (6840699812532384661) -->
+ <skip />
<string name="available" msgid="6007778121920339498">"可用"</string>
<string name="blocked" msgid="9195547604866033708">"已禁用"</string>
<string name="on" msgid="280241003226755921">"已开启"</string>
@@ -34,6 +36,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 +63,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>
@@ -114,8 +118,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear 不支持安装/卸载操作。"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"请选择要向&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;授予哪些权限"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;已更新。请选择要向此应用授予哪些权限。"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"取消"</string>
@@ -191,12 +193,14 @@
<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_limited_access" msgid="8824410215149764113">"允许有限访问"</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_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_with_device_name" msgid="7193042925656173271">"此应用在<xliff:g id="DEVICE_NAME">%2$s</xliff:g>上的<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>
@@ -204,7 +208,9 @@
<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_label_v3" msgid="693340578642156657">"管理闲置应用"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"移除权限、删除临时文件并停止发送通知"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"撤消权限、删除临时文件、停止发送通知并归档应用"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"为了保护您的数据,如果您连续几个月未使用此应用,系统会移除它的权限。"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"为了保护您的数据,如果您连续几个月未使用此应用,系统会移除其以下权限:<xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"为了保护您的数据,对于您连续几个月未使用过的应用,系统已将其权限移除。"</string>
@@ -219,7 +225,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 +238,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>
@@ -252,6 +258,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"获准管理所有文件"</string>
<string name="ask_header" msgid="2633816846459944376">"每次都询问"</string>
<string name="denied_header" msgid="903209608358177654">"不允许"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>上的<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"查看更多可以访问所有文件的应用"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 天}other{# 天}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 小时}other{# 小时}}"</string>
@@ -401,6 +408,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"默认钱包应用"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"钱包应用"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"钱包应用可以存储信用卡和会员卡、车钥匙和其他内容,帮助您完成各种形式的交易。"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"要将“<xliff:g id="APP_NAME">%1$s</xliff:g>”设为默认钱包应用?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"无需任何权限"</string>
<string name="request_role_current_default" msgid="738722892438247184">"当前默认应用"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"不再询问"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"设为默认应用"</string>
@@ -426,6 +438,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"更多默认应用"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"打开链接"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"默认工作应用"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"私密空间的默认应用"</string>
<string name="default_app_none" msgid="9084592086808194457">"无"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(系统默认)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"没有应用"</string>
@@ -455,48 +468,75 @@
<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_device_aware_storage_isolated" msgid="6463062962458809752">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_contacts" msgid="731025863972535928">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="6075412127429878638">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;获取&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_location" msgid="1264484517831380016">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;获取&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"要更改&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_nearby_devices" msgid="5293478278408567442">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"要将<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_coarselocation" msgid="8367540370912066757">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;获取&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sms" msgid="6639977653040502291">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_storage" msgid="6933251810928606636">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_aural" msgid="7927884506238101064">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_read_media_visual" msgid="3122576538319059333">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="8821701550505437951">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_microphone" msgid="3321823187623762958">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"要更改&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="5340173564041615494">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_camera" msgid="8533353179594971475">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"要更改&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_calllog" msgid="8220927190376843309">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_phone" msgid="590399263670349955">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_sensors" msgid="3874451050573615157">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;上访问与您的生命体征相关的传感器数据吗?"</string>
+ <string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"如要允许此应用随时可以访问身体传感器数据(即使在您未使用此应用时),请"<annotation id="link">"前往设置"</annotation>"。"</string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"始终允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”在使用中时访问身体传感器数据?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"要继续允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;于使用期间在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"要允许&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在&lt;b&gt;<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +552,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"无"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"过去 24 小时\n"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"过去\n7 天"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>百分之 <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”受 Android 保护。由于您的数据是在此设备上处理,因此状态栏或隐私信息中心不会显示这个应用的权限使用情况。"</string>
<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>
@@ -520,6 +561,13 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"摄像头访问权限已关闭"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"麦克风使用权限已关闭"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"位置信息获取权限已关闭"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"信息娱乐应用"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"必需的应用"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"此应用为必需应用"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"此应用为汽车制造商要求的必需应用"</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>
@@ -607,10 +655,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>
@@ -619,4 +667,26 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"<xliff:g id="TIME_DATE">%1$s</xliff:g>访问过"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"昨天<xliff:g id="TIME_DATE">%1$s</xliff:g>访问过"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>访问过"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"您的动态密码为 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限制的设置"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"出于安全考虑,此设置目前不可用。"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系统已拒绝向此应用授予<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>访问权限"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"该应用请求获得敏感权限,授予这项权限可能会导致您的个人信息和财务信息面临风险。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果不授予这项受限权限,该应用可能无法正常运行。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;了解如何授予访问权限&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系统已拒绝向此应用授予作为默认<xliff:g id="ROLE_NAME">%1$s</xliff:g>的访问权限"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"该应用请求获得敏感权限,授予这项权限可能会导致您的个人信息和财务信息面临风险。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果不授予这些受限权限,该应用可能无法正常运行。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;了解如何授予访问权限&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"系统已拒绝向此应用授予访问权限"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"如果授予此权限,您的个人信息和财务信息可能会面临风险。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果不授予这项受限权限,该应用可能无法正常运行。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;了解如何授予访问权限&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"了解详情"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"确定"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"权限请求被阻止"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"此应用请求获得额外的权限,但在流式传输会话期间无法授予权限。请先在手机上授予相应权限。"</string>
+ <!-- no translation found for privacy_dashboard_emergency_location_enforced_attribution_label (5702912511473457693) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_title (849723944428031911) -->
+ <skip />
+ <!-- no translation found for privacy_dashboard_emergency_location_dialog_description (5815970230573483329) -->
+ <skip />
</resources>
diff --git a/PermissionController/res/values-zh-rHK-v34/strings.xml b/PermissionController/res/values-zh-rHK-v34/strings.xml
index 65ad05bdb..12f494881 100644
--- a/PermissionController/res/values-zh-rHK-v34/strings.xml
+++ b/PermissionController/res/values-zh-rHK-v34/strings.xml
@@ -17,11 +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="location_settings" msgid="8863940440881290182">"位置資料存取權"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"適用於應用程式和服務。如果關閉此設定,系統仍會在你撥打緊急電話號碼時提供麥克風的資料"</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"應用程式和服務"</string>
</resources>
diff --git a/PermissionController/res/values-zh-rHK-watch/strings.xml b/PermissionController/res/values-zh-rHK-watch/strings.xml
index abe2f31f9..01f23b65c 100644
--- a/PermissionController/res/values-zh-rHK-watch/strings.xml
+++ b/PermissionController/res/values-zh-rHK-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"不可變更"</string>
<string name="generic_yes" msgid="2489207724988649846">"是"</string>
<string name="generic_cancel" msgid="2631708607129269698">"取消"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"一律允許"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"使用應用程式時"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"一律允許"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"使用應用程式時"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"一律允許"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"使用應用程式時"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"一律允許"</string>
</resources>
diff --git a/PermissionController/res/values-zh-rHK/strings.xml b/PermissionController/res/values-zh-rHK/strings.xml
index 435ead8ae..7110d9681 100644
--- a/PermissionController/res/values-zh-rHK/strings.xml
+++ b/PermissionController/res/values-zh-rHK/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"權限"</string>
<string name="cancel" msgid="8943320028373963831">"取消"</string>
<string name="back" msgid="6249950659061523680">"返回"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"關閉"</string>
<string name="available" msgid="6007778121920339498">"可用"</string>
<string name="blocked" msgid="9195547604866033708">"已封鎖"</string>
<string name="on" msgid="280241003226755921">"開啟"</string>
@@ -34,6 +35,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 +62,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear 不支援安裝/解除安裝操作。"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"選擇允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取的內容"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"已更新「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;。選擇允許此應用程式存取的內容。"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"取消"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"允許有限存取"</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_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_with_device_name" msgid="7193042925656173271">"此應用程式在<xliff:g id="DEVICE_NAME">%2$s</xliff:g>上的<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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"管理應用程式 (如未使用)"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"移除權限、刪除暫存檔案和停止通知"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"移除權限、刪除暫存檔案、停止通知和封存應用程式"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"為保護你的資料,系統已移除你在過去幾個月未曾使用的應用程式的權限。"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"為保護你的資料,如果應用程式在過去幾個月未曾使用,系統將會移除以下權限:<xliff:g id="PERMS">%1$s</xliff:g>。"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"為保護你的資料,系統已移除你在過去幾個月未曾使用的應用程式的權限。"</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"允許管理所有檔案"</string>
<string name="ask_header" msgid="2633816846459944376">"每次都詢問"</string>
<string name="denied_header" msgid="903209608358177654">"不允許"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>上的<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"顯示更多可存取所有檔案的應用程式"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 天}other{# 天}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 小時}other{# 小時}}"</string>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"預設錢包應用程式"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"錢包應用程式"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"錢包應用程式可存放信用卡、會員卡、車匙和其他資料,助你完成各種形式的交易。"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設定為預設錢包應用程式嗎?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"無需任何權限"</string>
<string name="request_role_current_default" msgid="738722892438247184">"目前預設"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"不要再詢問"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"設定為預設"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"更多預設應用程式"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"開啟連結"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"預設用於工作"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"私人空間的預設應用程式"</string>
<string name="default_app_none" msgid="9084592086808194457">"無"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(系統預設)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"沒有應用程式"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"顯示「Google 助理」觸發偵測"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"使用麥克風啟用語音助手時,在狀態列中顯示圖示"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你裝置上的相片和媒體嗎?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的相片和媒體嗎?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的聯絡人嗎?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的通訊錄嗎?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取此裝置的位置資訊嗎?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的位置嗎?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"此應用程式目前只有你在使用時才能存取位置資訊"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取此裝置的位置資訊嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的位置嗎?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"此應用程式可能想一直存取你的位置 (包括你沒有使用此應用程式時)。"<annotation id="link">"在設定中允許存取。"</annotation></string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」的位置存取權嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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_device_aware_nearby_devices" msgid="5293478278408567442">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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_device_aware_fineupgrade" msgid="4453775952305587571">"要將「<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>」在你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的位置存取權從概略位置變更為精確位置嗎?"</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_device_aware_coarselocation" msgid="8367540370912066757">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$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_calendar" msgid="1493150855673603806">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的日曆嗎?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的日曆嗎?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;傳送和查看短訊嗎?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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_device_aware_storage" msgid="6933251810928606636">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$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_visual" msgid="5548780620779729975">"要允許 &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; 存取此裝置上的相片和影片嗎?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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_device_aware_read_media_visual" msgid="3122576538319059333">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$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="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上錄音嗎?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"此應用程式將只能在你使用期間錄音"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;錄音嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上錄音嗎?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"此應用程式可能會要求隨時錄音 (包括你沒有使用此應用程式時)。"<annotation id="link">"在設定中允許存取。"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;?的麥克風存取權嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的麥克風存取權嗎?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"此應用程式想隨時錄音 (包括你沒有使用此應用程式時)。"<annotation id="link">"在設定中允許存取。"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的體能活動嗎?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的體能活動嗎?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;拍照和錄製影片嗎?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上拍照和錄製影片嗎?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"此應用程式將只能在你使用期間拍照及錄影"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;拍照和錄製影片嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上拍照和錄製影片嗎?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"此應用程式可能會要求隨時拍照及錄影 (包括你沒有使用此應用程式時)。"<annotation id="link">"在設定中允許存取。"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;?的相機存取權嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的相機存取權嗎?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"此應用程式想隨時拍照及錄影 (包括你沒有使用此應用程式時)。"<annotation id="link">"在設定中允許存取。"</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的手機通話記錄嗎?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上的手機通話記錄嗎?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;撥打電話和管理通話嗎?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;上撥打和管理電話通話嗎?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取與你身體機能相關的感應器資料嗎?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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>」應用程式&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="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"要繼續允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在應用程式使用期間存取「<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"無"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"過去\n24 小時"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"過去\n7 天內"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> 百分之 <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"<xliff:g id="APP_NAME">%1$s</xliff:g> 受 Android 保護。系統會在此裝置上處理你的資料,因此狀態列或私隱資訊主頁不會顯示此應用程式的權限使用情況。"</string>
<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>
@@ -520,7 +560,14 @@
<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="automotive_blocked_camera_title" msgid="6142362431548829416">"攝錄機權限已關閉"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"已關閉麥克風存取權"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"已關閉位置存取權"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"資訊娛樂應用程式無法存取"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"必要應用程式無法存取"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"需要使用此應用程式"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"汽車製造商需要使用此應用程式"</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 +578,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"上次存取時間:<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"上次存取時間:昨天<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"上次存取時間:<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"你的一次性密碼是 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限設定"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"為安全起見,系統目前不提供此設定。"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系統已拒絕授予應用程式「<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>」存取權"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"應用程式要求存取敏感資料權限,授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;進一步瞭解如何授予存取權&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式預設<xliff:g id="ROLE_NAME">%1$s</xliff:g>存取權"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"應用程式要求存取敏感資料權限,授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;進一步瞭解如何授予存取權&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"系統已拒絕授予應用程式存取權"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"授予此權限可能會危害你的個人和財務資料。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如沒有此受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;進一步瞭解如何授予存取權&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"瞭解詳情"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"確定"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"權限要求被拒"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"此應用程式要求額外的權限,但串流工作階段期間無法授予權限。請先在手機上授予權限。"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"供緊急電話或短訊使用"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"已傳送位置給緊急服務"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"此應用程式在你與緊急電話號碼通話或收發短訊期間存取過你的裝置位置。即使應用程式沒有位置權限或裝置位置已關閉,也可能會發生這種情況。"<a href="https://support.google.com/android/answer/9319337">"瞭解詳情"</a></string>
</resources>
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 533a95e78..cebdecbaa 100644
--- a/PermissionController/res/values-zh-rTW-v34/strings.xml
+++ b/PermissionController/res/values-zh-rTW-v34/strings.xml
@@ -23,5 +23,4 @@
<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>
</resources>
diff --git a/PermissionController/res/values-zh-rTW-watch/strings.xml b/PermissionController/res/values-zh-rTW-watch/strings.xml
index 2a91aa040..8173cc23f 100644
--- a/PermissionController/res/values-zh-rTW-watch/strings.xml
+++ b/PermissionController/res/values-zh-rTW-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"無法變更"</string>
<string name="generic_yes" msgid="2489207724988649846">"是"</string>
<string name="generic_cancel" msgid="2631708607129269698">"取消"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"一律允許"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"僅限使用應用程式時"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"一律允許"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"僅限使用應用程式時"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"一律允許"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"僅限使用應用程式時"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"一律允許"</string>
</resources>
diff --git a/PermissionController/res/values-zh-rTW/strings.xml b/PermissionController/res/values-zh-rTW/strings.xml
index 422b072a8..eb21e61d7 100644
--- a/PermissionController/res/values-zh-rTW/strings.xml
+++ b/PermissionController/res/values-zh-rTW/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"權限"</string>
<string name="cancel" msgid="8943320028373963831">"取消"</string>
<string name="back" msgid="6249950659061523680">"返回"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"關閉"</string>
<string name="available" msgid="6007778121920339498">"可用"</string>
<string name="blocked" msgid="9195547604866033708">"已封鎖"</string>
<string name="on" msgid="280241003226755921">"開啟"</string>
@@ -34,6 +35,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 +54,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 +62,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 +73,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 +86,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>
@@ -114,8 +117,6 @@
<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>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Wear 不支援安裝及解除安裝操作。"</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"選擇要授予「<xliff:g id="APP_NAME">%1$s</xliff:g>」的存取權"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已更新。請選擇要授予這個應用程式的存取權。"</string>
<string name="review_button_cancel" msgid="2191147944056548886">"取消"</string>
@@ -191,12 +192,14 @@
<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_limited_access" msgid="8824410215149764113">"允許有限存取"</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_header_with_device_name" msgid="7193042925656173271">"這個應用程式在<xliff:g id="DEVICE_NAME">%2$s</xliff:g>上的<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>
@@ -204,7 +207,9 @@
<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_label_v3" msgid="693340578642156657">"管理未使用的應用程式"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"移除權限、刪除暫存檔及停止通知"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"移除權限、刪除暫存檔、停止通知並封存應用程式"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"為了保護你的資料,如果你連續幾個月都未使用這個應用程式,系統會移除其權限。"</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"為了保護你的資料,如果應用程式已有數個月未使用,系統將移除以下權限:<xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"為了保護你的資料,針對數個月未使用的應用程式,系統已移除相關權限。"</string>
@@ -219,7 +224,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>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"可管理所有檔案"</string>
<string name="ask_header" msgid="2633816846459944376">"每次都詢問"</string>
<string name="denied_header" msgid="903209608358177654">"不允許"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>上的<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"查看可存取所有檔案的其他應用程式"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{1 天}other{# 天}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{# 小時}other{# 小時}}"</string>
@@ -347,21 +353,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 +379,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>
@@ -401,6 +407,11 @@
<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="role_wallet_label" msgid="3719419175656204207">"預設錢包應用程式"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"錢包應用程式"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"錢包應用程式可儲存信用卡、會員卡、車子鑰匙和其他資訊,方便你以多種形式進行交易。"</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"要將「<xliff:g id="APP_NAME">%1$s</xliff:g>」設為預設錢包應用程式嗎?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"無需任何權限"</string>
<string name="request_role_current_default" msgid="738722892438247184">"目前的預設應用程式"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"不要再詢問"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"設為預設"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"更多預設應用程式"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"開啟連結"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"預設的工作應用程式"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"私人空間的預設應用程式"</string>
<string name="default_app_none" msgid="9084592086808194457">"無"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(系統預設)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"沒有可用的應用程式"</string>
@@ -455,48 +467,75 @@
<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">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取裝置中的相片和媒體嗎?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取相片和媒體嗎?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取你的聯絡人嗎?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的聯絡人嗎?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置的位置資訊嗎?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的位置資訊嗎?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"該應用程式只有在你使用時,才能存取位置資訊"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置的位置資訊嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的位置資訊嗎?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"這個應用程式可能會要求隨時存取你的位置資訊 (即使在你未使用此應用程式時)。"<annotation id="link">"如要授予權限,請前往「設定」"</annotation>"。"</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」的位置資訊存取權嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的位置資訊存取權嗎?"</string>
<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="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$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_device_aware_fineupgrade" msgid="4453775952305587571">"要將「<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>」在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的位置資訊存取權從大概變更為精確嗎?"</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_device_aware_coarselocation" msgid="8367540370912066757">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取「<xliff:g id="DEVICE_NAME">%2$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_device_aware_calendar" msgid="7161929851377463612">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的日曆嗎?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」傳送及查看簡訊嗎?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;傳送及查看簡訊嗎?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取裝置中的相片、媒體和檔案嗎?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取相片、媒體和檔案嗎?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置上的&lt;b&gt;相片、影片、音樂和音訊&lt;/b&gt;嗎?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置上的&lt;b&gt;相片、影片、音樂、音訊和其他檔案&lt;/b&gt;嗎?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取這部裝置上的音樂和音訊嗎?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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_device_aware_read_media_visual" msgid="3122576538319059333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$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_device_aware_more_photos" msgid="1703469013613723053">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$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>」錄音嗎?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;使用「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的麥克風錄音嗎?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"這個應用程式只有在你使用時才能錄音"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」錄音嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;使用「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的麥克風錄音嗎?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"這個應用程式要求取得隨時都能錄音的權限,即使你當下並未使用該應用程式。"<annotation id="link">"如要授予權限,請前往設定頁面。"</annotation></string>
<string name="permgroupupgraderequest_microphone" msgid="1362781696161233341">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」的麥克風存取權嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的麥克風存取權嗎?"</string>
<string name="permgroupupgraderequestdetail_microphone" msgid="2870497719571464239">"這個應用程式想取得隨時都能錄音的權限,即使你當下並未使用該應用程式。"<annotation id="link">"如要授予權限,請前往設定頁面。"</annotation></string>
<string name="permgrouprequest_activityRecognition" msgid="5415121592794230330">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的體能活動記錄嗎?"</string>
+ <string name="permgrouprequest_device_aware_activityRecognition" msgid="1243869530588745374">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的體能活動資料嗎?"</string>
<string name="permgrouprequest_camera" msgid="5123097035410002594">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」拍攝相片及錄製影片嗎?"</string>
+ <string name="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;使用「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的相機拍照及錄影嗎?"</string>
<string name="permgrouprequestdetail_camera" msgid="9085323239764667883">"這個應用程式只有在你使用時,才能拍照及錄影"</string>
<string name="permgroupbackgroundrequest_camera" msgid="1274286575704213875">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」拍照及錄影嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;使用「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的相機拍照及錄影嗎?"</string>
<string name="permgroupbackgroundrequestdetail_camera" msgid="4458783509089859078">"這個應用程式要求取得隨時都能拍照及錄影的權限,即使你當下並未使用該應用程式。"<annotation id="link">"如要授予權限,請前往設定頁面。"</annotation></string>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」的相機存取權嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"要變更「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;的相機存取權嗎?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"這個應用程式想取得隨時都能拍照及錄影的權限,即使你當下並未使用該應用程式。"<annotation id="link">"如要授予權限,請前往設定頁面。"</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的通話記錄嗎?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;存取你的通話記錄嗎?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」撥打電話及管理通話嗎?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&lt;/b&gt;撥打電話及管理通話嗎?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」存取與你生命徵象相關的感應器資料嗎?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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>」&lt;b&gt;&lt;/b&gt;存取與你生命徵象相關的感應器資料嗎?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;b&gt;&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>」&lt;b&gt;&lt;/b&gt;應用程式在使用期間存取人體感應器資料嗎?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"要繼續允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在使用期間存取「<xliff:g id="DEVICE_NAME">%2$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_device_aware_notifications" msgid="857671638951040514">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;b&gt;&lt;/b&gt;在「<xliff:g id="DEVICE_NAME">%2$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>
@@ -512,14 +551,22 @@
<string name="privdash_label_none" msgid="5991866260360484858">"無"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"過去\n24 小時"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"過去\n7 天內"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> 百分之 <xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」受 Android 保護。系統是在這部裝置上處理你的資料,因此狀態列或隱私資訊主頁不會顯示這個應用程式的權限使用情形。"</string>
<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_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="automotive_blocked_camera_title" msgid="6142362431548829416">"攝影機權限已關閉"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"麥克風存取權未開啟"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"位置資訊存取權未開啟"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"資訊娛樂應用程式無法存取"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"必要應用程式無法存取"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"這是必要的應用程式"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"這是汽車製造商要求使用的應用程式"</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>
@@ -579,9 +626,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 +654,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>
@@ -619,4 +666,23 @@
<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>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"上次存取時間:<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"上次存取時間:昨天<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"上次存取時間:<xliff:g id="TIME_DATE_0">%1$s</xliff:g><xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"你的動態密碼為 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"受限制的設定"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"為了安全起見,目前無法使用這項設定。"</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"系統已拒絕授予應用程式「<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>」存取權"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"這個應用程式要求取得私密資訊權限,授予這項權限可能導致你的個人資訊和財務資訊面臨風險。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果未取得這項受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;瞭解如何授予權限&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"系統已拒絕授予應用程式做為預設「<xliff:g id="ROLE_NAME">%1$s</xliff:g>」的存取權"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"這個應用程式要求取得私密資訊權限,授予這些權限可能導致你的個人資訊和財務資訊面臨風險。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果未取得這些受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;瞭解如何授予權限&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"系統已拒絕將存取權授予應用程式"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"如果授予這項權限,你的個人資訊和財務資訊可能會面臨風險。<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>如果未取得這項受限制權限,應用程式可能無法正常運作。&lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;瞭解如何授予權限&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"瞭解詳情"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"確定"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"權限要求遭拒"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"這個應用程式要求額外權限,但串流期間無法授權。請先在手機上授予權限。"</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"用於緊急電話或簡訊"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"位置資訊已傳送至緊急救援服務"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"這個應用程式已在你撥打緊急電話號碼或傳送簡訊期間,存取裝置的位置資訊。即使應用程式並未取得位置存取權,或是裝置定位功能已關閉,也可能發生這種情況。"<a href="https://support.google.com/android/answer/9319337">"瞭解詳情"</a></string>
</resources>
diff --git a/PermissionController/res/values-zu-v34/strings.xml b/PermissionController/res/values-zu-v34/strings.xml
index cb348a889..d960be6ee 100644
--- a/PermissionController/res/values-zu-v34/strings.xml
+++ b/PermissionController/res/values-zu-v34/strings.xml
@@ -23,5 +23,4 @@
<string name="health_connect_summary" msgid="815473513776882296">"Phatha ukufinyelela kwe-app kudatha yezempilo"</string>
<string name="location_settings" msgid="8863940440881290182">"Setha indawo"</string>
<string name="mic_toggle_description" msgid="1504101620086616040">"Okwama-app namasevisi. Uma leli sethingi livaliwe, idatha yemakrofoni ingabiwa uma ushayela inombolo yezimo eziphuthumayo."</string>
- <string name="location_settings_subtitle" msgid="6846532794702613851">"Okwama-app namasevisi"</string>
</resources>
diff --git a/PermissionController/res/values-zu-watch/strings.xml b/PermissionController/res/values-zu-watch/strings.xml
index 01b82fd1a..f314b0699 100644
--- a/PermissionController/res/values-zu-watch/strings.xml
+++ b/PermissionController/res/values-zu-watch/strings.xml
@@ -22,4 +22,11 @@
<string name="permission_summary_enforced_by_policy" msgid="2352478756952948019">"Akukwazi ukushintshwa"</string>
<string name="generic_yes" msgid="2489207724988649846">"Yebo"</string>
<string name="generic_cancel" msgid="2631708607129269698">"Khansela"</string>
+ <string name="permission_access_always" msgid="2107115233573823032">"Ngaso sonke isikhathi"</string>
+ <string name="permission_access_only_foreground" msgid="4412115020089923986">"Ngenkathi isebenzisa i-app"</string>
+ <string name="app_permission_button_allow_always" msgid="4920899432212307102">"Ngaso sonke isikhathi"</string>
+ <string name="app_permission_button_allow_foreground" msgid="7186980598244864830">"Ngenkathi isebenzisa i-app"</string>
+ <string name="grant_dialog_button_allow_always" msgid="7130695257254694576">"Ngaso sonke isikhathi"</string>
+ <string name="grant_dialog_button_allow_foreground" msgid="8917595344037255090">"Ngenkathi isebenzisa i-app"</string>
+ <string name="grant_dialog_button_allow_background" msgid="6104993390936535493">"Ngaso sonke isikhathi"</string>
</resources>
diff --git a/PermissionController/res/values-zu/strings.xml b/PermissionController/res/values-zu/strings.xml
index 8d51046c9..3a47a253d 100644
--- a/PermissionController/res/values-zu/strings.xml
+++ b/PermissionController/res/values-zu/strings.xml
@@ -21,6 +21,7 @@
<string name="permission_search_keyword" msgid="1214451577494730543">"izimvume"</string>
<string name="cancel" msgid="8943320028373963831">"Khansela"</string>
<string name="back" msgid="6249950659061523680">"Emuva"</string>
+ <string name="dialog_close" msgid="6840699812532384661">"Vala"</string>
<string name="available" msgid="6007778121920339498">"Iyatholakala"</string>
<string name="blocked" msgid="9195547604866033708">"Uvinjiwe"</string>
<string name="on" msgid="280241003226755921">"Vuliwe"</string>
@@ -34,6 +35,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 +52,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 +62,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 +93,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>
@@ -114,8 +117,6 @@
<string name="all_permissions" msgid="6911125611996872522">"Zonke izimvume"</string>
<string name="other_permissions" msgid="2901186127193849594">"Amanye amakhono wohlelo lokusebenza"</string>
<string name="permission_request_title" msgid="8790310151025020126">"Isicelo semvume"</string>
- <string name="wear_not_allowed_dlg_title" msgid="1429467891296932713">"I-Android Wear"</string>
- <string name="wear_not_allowed_dlg_text" msgid="512340555334769098">"Izenzo zokufaka/ukukhipha azisekelwe ku-Wear."</string>
<string name="permission_review_title_template_install" msgid="1284337937156289081">"Khetha ukuthi uzovumela ini ukuthi i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ifinyelele kuyo"</string>
<string name="permission_review_title_template_update" msgid="3232333580548588657">"I-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ibuyekeziwe. Khetha ukuthi uzovumela ini ukuthi ifinyelelwe ilolu hlelo lokusebenza."</string>
<string name="review_button_cancel" msgid="2191147944056548886">"Khansela"</string>
@@ -191,12 +192,14 @@
<string name="app_permission_button_always_allow_all" msgid="4905699259378428855">"Njalo vumela konke"</string>
<string name="app_permission_button_ask" msgid="3342950658789427">"Buza njalo"</string>
<string name="app_permission_button_deny" msgid="6016454069832050300">"Ungavumeli"</string>
+ <string name="app_permission_button_allow_limited_access" msgid="8824410215149764113">"Vumela ukufinyelela okulinganiselwe"</string>
<string name="precise_image_description" msgid="6349638632303619872">"Indawo eqondile"</string>
<string name="approximate_image_description" msgid="938803699637069884">"Indawo elinganiselwayo"</string>
<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">"ukufinyelela kwale app ku<xliff:g id="PERM">%1$s</xliff:g>"</string>
+ <string name="app_permission_header_with_device_name" msgid="7193042925656173271">"Ukufinyelela kwe-<xliff:g id="PERM">%1$s</xliff:g> kwale app kuvuliwe ku-<xliff:g id="DEVICE_NAME">%2$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>
@@ -204,7 +207,9 @@
<string name="auto_revoke_label" msgid="5068393642936571656">"Susa izimvume uma uhlelo lokusebenza lungasetshenziswa"</string>
<string name="unused_apps_label" msgid="2595428768404901064">"Susa izimvume uphinde ukhulule isikhala"</string>
<string name="unused_apps_label_v2" msgid="7058776770056517980">"Misa okwesikhashana umsebenzi we-app uma ingasetshenziswa"</string>
+ <string name="unused_apps_label_v3" msgid="693340578642156657">"Lawula i-app uma ingasetsheziswa"</string>
<string name="unused_apps_summary" msgid="8839466950318403115">"Susa izimvume, sula amafayela wesikhashana, futhi umise izaziso"</string>
+ <string name="unused_apps_summary_v2" msgid="5011313200815115802">"Susa izimvume, sula amafayela esikhashana, futhi ufake i-app kungobo yomlando"</string>
<string name="auto_revoke_summary" msgid="5867548789805911683">"Ukuze kuvikelwe idatha yakho, izimvume zalolu hlelo lokusebenza zizosuswa uma uhlelo lokusebenza lungasetshenziswa izinyanga ezimbalwa."</string>
<string name="auto_revoke_summary_with_permissions" msgid="389712086597285013">"Ukuze kuvikelwe idatha yakho, uma uhlelo lokusebenza lungasetshenzisiwe, izimvume ezilandelayo zizosuswa: <xliff:g id="PERMS">%1$s</xliff:g>"</string>
<string name="auto_revoked_apps_page_summary" msgid="6594753657893756536">"Ukuze kuvikelwe idatha yakho, izimvume zisusiwe kusukela kuzinhlelo zokusebenza ongazange uzisebenzise ezinyangeni ezimbalwa."</string>
@@ -252,6 +257,7 @@
<string name="allowed_storage_full" msgid="5356699280625693530">"Kuvumelekile ukuphatha wonke amafayela"</string>
<string name="ask_header" msgid="2633816846459944376">"Buza njalo"</string>
<string name="denied_header" msgid="903209608358177654">"Akuvumelekile"</string>
+ <string name="permission_group_name_with_device_name" msgid="8798741850536024820">"I-<xliff:g id="PERM_GROUP_NAME">%1$s</xliff:g> ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
<string name="storage_footer_hyperlink_text" msgid="8873343987957834810">"Bona ama-app engeziwe akwazi ukufinyelela wonke amafayela"</string>
<string name="days" msgid="609563020985571393">"{count,plural, =1{usuku 1}one{izinsuku #}other{izinsuku #}}"</string>
<string name="hours" msgid="7302866489666950038">"{count,plural, =1{Ihora elingu-#}one{Amahora angu-#}other{Amahora angu-#}}"</string>
@@ -401,6 +407,11 @@
<string name="role_notes_short_label" msgid="8796604147546125285">"I-App yamanothi"</string>
<string name="role_notes_description" msgid="8496852798616883551">"Ama-app akuvumela ukuthi uthathe amanothi kudivayisi yakho"</string>
<string name="role_notes_search_keywords" msgid="7710756695666744631">"amanothi"</string>
+ <string name="role_wallet_label" msgid="3719419175656204207">"I-app ye-wallet engokuzenzakalelayo"</string>
+ <string name="role_wallet_short_label" msgid="6521288403762457452">"I-app ye-wallet"</string>
+ <string name="role_wallet_description" msgid="3726535836165949838">"Ama-app e-Wallet angagcina amakhredithi kanye namakhadi obuqotho akho, izikhiye zemoto kanye nezinye izino ukukusiza ngezinhlobo ezihlukahlukene zokuthenga."</string>
+ <string name="role_wallet_request_title" msgid="4770217108262737093">"Setha i-<xliff:g id="APP_NAME">%1$s</xliff:g> njenge-app ye-wallet yakho ezenzakalelayo?"</string>
+ <string name="role_wallet_request_description" msgid="6305487425777483053">"Azikho izimvume ezidingekayo"</string>
<string name="request_role_current_default" msgid="738722892438247184">"Okuzenzakalelayo kwamanje"</string>
<string name="request_role_dont_ask_again" msgid="3556017886029520306">"Ungabuzi futhi"</string>
<string name="request_role_set_as_default" msgid="4253949643984172880">"Setha njengokuzenzekelayo"</string>
@@ -426,6 +437,7 @@
<string name="default_apps_more" msgid="4078194675848858093">"Okuzenzakalelayo okuningi"</string>
<string name="default_apps_manage_domain_urls" msgid="6775566451561036069">"Ivula amalinki"</string>
<string name="default_apps_for_work" msgid="4970308943596201811">"Okuzenzakalelayo kokusebenza"</string>
+ <string name="default_apps_for_private_profile" msgid="2022024112144880785">"Okuzenzakalelayo kwendawo engasese"</string>
<string name="default_app_none" msgid="9084592086808194457">"Lutho"</string>
<string name="default_app_system_default" msgid="6218386768175513760">"(Okuzenzakalelayo kwesistimu)"</string>
<string name="default_app_no_apps" msgid="115720991680586885">"Azikho izinhlelo zokusebenza"</string>
@@ -455,48 +467,75 @@
<string name="assistant_record_audio_user_sensitive_title" msgid="5532123360322362378">"Bonisa ukutholwa okucushwe umsizi"</string>
<string name="assistant_record_audio_user_sensitive_summary" msgid="6482937591816401619">"Bonisa isithonjana kwibha yesaziso uma imakrofoni isetshenzisiwe ukusebenzisa umsizi wezwi"</string>
<string name="permgrouprequest_storage_isolated" msgid="4892154224026852295">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele izithombe kanye nemidiya kudivayisi yakho?"</string>
+ <string name="permgrouprequest_device_aware_storage_isolated" msgid="6463062962458809752">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele izithombe nemidiya &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_contacts" msgid="8391550064551053695">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithole ukufinyelela koxhumana nabo?"</string>
+ <string name="permgrouprequest_device_aware_contacts" msgid="731025863972535928">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele oxhumana nabo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_location" msgid="6990232580121067883">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kundawo yale divayisi?"</string>
+ <string name="permgrouprequest_device_aware_location" msgid="6075412127429878638">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele indawo &lt;b&gt;ye-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_location" msgid="2635935335778429894">"Uhlelo lokusebenza luzoba nokufinyelela kuphela kundawo ngenkathi usebenzisa uhlelo lokusebenza"</string>
<string name="permgroupbackgroundrequest_location" msgid="1085680897265734809">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kundawo yale divayisi?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_location" msgid="1264484517831380016">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele indawo &lt;b&gt;ye-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>’s&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="8021219324989662957">"Lolu hlelo lokusebenza lungafuna ukufinyelela indawo yakho ngaso sonke isikhathi, ngisho noma ungasebenzisi uhlelo lokusebenza. "<annotation id="link">"Vumela kokuthi izilungiselelo"</annotation>"."</string>
<string name="permgroupupgraderequest_location" msgid="8328408946822691636">"Shintsha ukufinyelela kwendawo kwe-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_location" msgid="1812338666887726191">"Shintsha ukufinyelela indawo &lt;b&gt;kwe-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_location" msgid="1550899076845189165">"Lolu hlelo lokusebenza lufuna ukufinyelela kundawo yakho ngaso sonke isikhathi, nanoma ungasebenzisi uhlelo lokusebenza. "<annotation id="link">"Vumela kokuthi izilungiselelo"</annotation>"."</string>
<string name="permgrouprequest_nearby_devices" msgid="2272829282660436700">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithole, ixhume, futhi anqume ukuma okuhlobene kwamadivayisi aseduze?"</string>
+ <string name="permgrouprequest_device_aware_nearby_devices" msgid="5293478278408567442">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithole, ixhume, futhi ibone ukuthi angakuphi nendawo amadivayisi aseduze &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_nearby_devices" msgid="6877531270654738614">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithole, ixhume, futhi anqume ukuma okuhlobene kwamadivayisi aseduze? "<annotation id="link">"Vumela kumasethingi."</annotation></string>
<string name="permgrouprequest_fineupgrade" msgid="2334242928821697672">"Shintsha ukufinyelela indawo kwe-<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> kusuka kokulinganiselwayo kuya kokunembile?"</string>
+ <string name="permgrouprequest_device_aware_fineupgrade" msgid="4453775952305587571">"Shintsha ukufinyelela kwendawo kwe-<xliff:g id="APP_NAME">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g> &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&amp;gt yakho; kusuka ekulinganiseni kuya kokunembayo?"</string>
<string name="permgrouprequest_coarselocation" msgid="7244605063736425232">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele indawo elinganiselwe yale divayisi?"</string>
+ <string name="permgrouprequest_device_aware_coarselocation" msgid="8367540370912066757">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele indawo elinganiselwe &lt;b&gt;i-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; ekuyo?"</string>
<string name="permgrouprequest_finelocation_imagetext" msgid="1313062433398914334">"Kunembile"</string>
<string name="permgrouprequest_coarselocation_imagetext" msgid="8650605041483025297">"Cishe"</string>
<string name="permgrouprequest_calendar" msgid="1493150855673603806">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kukhalenda yakho?"</string>
+ <string name="permgrouprequest_device_aware_calendar" msgid="7161929851377463612">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele ikhalenda lakho &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sms" msgid="5672063688745420991">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithumele iphinde ibuke imilayezo ye-SMS?"</string>
+ <string name="permgrouprequest_device_aware_sms" msgid="6639977653040502291">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithumele futhi ibuke imiyalezo ye-SMS &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage" msgid="8717773092518621602">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kuzithombe, imidiya, namafayela akudivayisi yakho?"</string>
+ <string name="permgrouprequest_device_aware_storage" msgid="6933251810928606636">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele izithombe, imidiya, namafayela &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_storage_q_to_s" msgid="8213701872983685505">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe, amavidiyo, umculo, nokulalelwayo, kule divayisi?"</string>
<string name="permgrouprequest_storage_pre_q" msgid="168130651144569428">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe, amavidiyo, umculo, okulalelwayo, namanye amafayela kule divayisi?"</string>
<string name="permgrouprequest_read_media_aural" msgid="2593365397347577812">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele umculo nokulalelwayo kule divayisi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_aural" msgid="7927884506238101064">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele umculo nomsindo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_read_media_visual" msgid="5548780620779729975">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukuba ifinyelele izithombe namavidiyo kule divayisi?"</string>
+ <string name="permgrouprequest_device_aware_read_media_visual" msgid="3122576538319059333">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele izithombe namavidiyo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_more_photos" msgid="128933814654231321">"Vumela &lt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&gt; ukuba ifinyelele izithombe namavidiyo kule divayisi?"</string>
+ <string name="permgrouprequest_device_aware_more_photos" msgid="1703469013613723053">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele izithombe namavidiyo engeziwe &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_microphone" msgid="2825208549114811299">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo?"</string>
+ <string name="permgrouprequest_device_aware_microphone" msgid="8821701550505437951">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequestdetail_microphone" msgid="8510456971528228861">"Uhlelo lokusebenza luzokwazi ukurekhoda imisindo kuphela kuyilapho usebenzisa uhlelo lokusebenza"</string>
<string name="permgroupbackgroundrequest_microphone" msgid="8874462606796368183">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_microphone" msgid="3321823187623762958">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi irekhode umsindo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_microphone" msgid="553702902263681838">"Lolu hlelo lokusebenza lungafuna ukurekhoda imisindo ngaso sonke isikhathi, ngisho nalapho ungasebenzisi uhlelo lokusebenza. "<annotation id="link">"Vumela kumasethingi."</annotation></string>
<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="permgroupupgraderequest_device_aware_microphone" msgid="8722411173971679806">"Shintsha ukufinyelela imakrofoni &lt;b&gt;kwe-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$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_device_aware_activityRecognition" msgid="1243869530588745374">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele umsebenzi womzimba wakho &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgrouprequest_device_aware_camera" msgid="5340173564041615494">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithathe izithombe futhi irekhode ividiyo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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="permgroupbackgroundrequest_device_aware_camera" msgid="8533353179594971475">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ithathe izithombe futhi irekhode ividiyo &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</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>
<string name="permgroupupgraderequest_camera" msgid="640758449200241582">"Shintsha ukufinyelela kwekhamera kwe-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;?"</string>
+ <string name="permgroupupgraderequest_device_aware_camera" msgid="3290160912843715236">"Shintsha ukufinyelela ikhamera &lt;b&gt;kwe-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_camera" msgid="6642747548010962597">"Lolu hlelo lokusebenza lufuna ukuthatha izithombe futhi lirekhode ividiyo ngaso sonke isikhathi, ngisho nalapho ungasebenzi uhlelo lokusebenza. "<annotation id="link">"Vumela kumasethingi."</annotation></string>
<string name="permgrouprequest_calllog" msgid="2065327180175371397">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukufinyelela kurekhodi lakho lamakholi wefoni?"</string>
+ <string name="permgrouprequest_device_aware_calllog" msgid="8220927190376843309">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele amarekhodi amakholi efoni yakho &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_phone" msgid="1829234136997316752">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi yenze iphinde iphathe amakholi efoni?"</string>
+ <string name="permgrouprequest_device_aware_phone" msgid="590399263670349955">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi yenze futhi ilawule amakholi efoni &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgrouprequest_sensors" msgid="4397358316850652235">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kudatha yenzwa emayelana nezimpawu zakho ezibalulekile?"</string>
+ <string name="permgrouprequest_device_aware_sensors" msgid="3874451050573615157">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele idatha yezinzwa mayelana nezimpawu zakho ezibalulekile zokusebenza komzimba wakho &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupupgraderequestdetail_sensors" msgid="6651914048792092835">"Le app ifuna ukufinyelela kudatha yenzwa emayelana nezimpawu zakho ezibalulekile ngaso sonke isikhathi, ngisho noma ungasebenzisi i-app. "<annotation id="link">"iya kumasethingi."</annotation></string>
<string name="permgroupbackgroundrequest_sensors" msgid="5661924322018503886">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele kudatha yenzwa emayelana nezimpawu zakho ezibalulekile?"</string>
+ <string name="permgroupbackgroundrequest_device_aware_sensors" msgid="3687673359121603824">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele idatha yezinzwa mayelana nezimpawu ezibalulekile zokusebenza komzimba wakho &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="permgroupbackgroundrequestdetail_sensors" msgid="7726767635834043501">"Ukuze uvumele le app ukuba ifinyelele idatha yenzwa yomzimba ngaso sonke isikhathi, ngisho nalapho ungasebenzisi i-app, "<annotation id="link">"iya kumasethingi."</annotation></string>
<string name="permgroupupgraderequest_sensors" msgid="7576527638411370468">"Qhubeka uvumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuba ifinyelele inzwa yedatha yomzimba kuyilapho i-app isebenza?"</string>
+ <string name="permgroupupgraderequest_device_aware_sensors" msgid="5542771499929819675">"Siqhubeke sivumela &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ifinyelele idatha yezinzwa zomzimba &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt; phakathi nokusetshenziswa kwe-app?"</string>
<string name="permgrouprequest_notifications" msgid="6396739062335106181">"Vumela i-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ikuthumele izaziso?"</string>
+ <string name="permgrouprequest_device_aware_notifications" msgid="857671638951040514">"Siyivumele &lt;b&gt;i-<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ukuthi ikuthumele izaziso &lt;b&gt;ku-<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/b&gt;?"</string>
<string name="auto_granted_permissions" msgid="6009452264824455892">"Izimvume ezilawuliwe"</string>
<string name="auto_granted_location_permission_notification_title" msgid="7570818224669050377">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> inokufinyelela kwendawo"</string>
<string name="auto_granted_permission_notification_body" msgid="5040234389205471318">"Inhlangano yakho ivumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukufinyelela indawo yakho"</string>
@@ -512,6 +551,7 @@
<string name="privdash_label_none" msgid="5991866260360484858">"Lutho"</string>
<string name="privdash_label_24h" msgid="1512532123865375319">"Emahoreni angama-\n24 adlule"</string>
<string name="privdash_label_7d" msgid="5645301995348656931">"Izinsuku ezi-\n7 ezedlule"</string>
+ <string name="privdash_usage_percent" msgid="6893824766124414127">"<xliff:g id="PERMISSION_NAME">%1$s</xliff:g> amaphesenti angu-<xliff:g id="PERCENT">%2$d</xliff:g>"</string>
<string name="exempt_mic_camera_info_label" msgid="6273581737010902815">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ivikelwe i-Android. Ngenxa yokuthi idatha yakho icutshungulwa kule divayisi, ukusetshenziswa kwemvume yale app akuboniswa kubha yesimo noma kudeshibhodi yakho yobumfihlo"</string>
<string name="exempt_info_label" msgid="6286190981253476699">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ivikelwe i-Android. Ngenxa yokuthi idatha yakho icutshungulwa kule divayisi, ukusetshenziswa kwemvume yale app akuboniswa kudeshibhodi yakho yobumfihlo"</string>
<string name="blocked_camera_title" msgid="1128510551791284384">"Ikhamera yedivayisi ivinjiwe"</string>
@@ -520,6 +560,13 @@
<string name="blocked_sensor_summary" msgid="4443707628305027375">"Okwama-app namasevisi"</string>
<string name="blocked_mic_summary" msgid="8960466941528458347">"Idatha yemakrofoni isengabiwa lapho wenza ikholi yenombolo yezimo eziphuthumayo"</string>
<string name="blocked_sensor_button_label" msgid="6742092634984289658">"Shintsha"</string>
+ <string name="automotive_blocked_camera_title" msgid="6142362431548829416">"Ukufinyelela kwekhamera kuvaliwe"</string>
+ <string name="automotive_blocked_microphone_title" msgid="3956311098238620220">"Ukufinyelelwa kwemakrofoni kuvaliwe"</string>
+ <string name="automotive_blocked_location_title" msgid="6047574747593264689">"Ukufinyelelwa kwendawo kuvaliwe"</string>
+ <string name="automotive_blocked_infotainment_app_summary" msgid="8217099645064950860">"Kuma app e-infotainment"</string>
+ <string name="automotive_blocked_required_app_summary" msgid="8591513745681168088">"Kuma-app adingekayo"</string>
+ <string name="automotive_required_app_title" msgid="2992168288249988735">"Le app iyadingeka"</string>
+ <string name="automotive_required_app_summary" msgid="6514902316658090465">"Le app idingwa umkhiqizi wemoto yakho"</string>
<string name="safety_center_dashboard_page_title" msgid="2810774008694315854">"Ukuvikeleka nobumfihlo"</string>
<string name="safety_center_rescan_button" msgid="4517514567809409596">"Skena idivayisi"</string>
<string name="safety_center_issue_card_dismiss_button" msgid="5113965506144222402">"Chitha"</string>
@@ -619,4 +666,23 @@
<string name="safety_label_changes_notification_title" msgid="4479955083472203839">"Izibuyekezo zokwabelana ngedatha"</string>
<string name="safety_label_changes_notification_desc" msgid="7808764283266234675">"Amanye ama-app aguqule indlela angabelana ngayo ngedatha yendawo yakho"</string>
<string name="safety_label_changes_gear_description" msgid="2655887555599138509">"Amasethingi"</string>
+ <string name="wear_app_perms_24h_access" msgid="8668121661337328895">"Kufinyelelwe ngo-<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_24h_access_yest" msgid="7069312481704735679">"Kufinyelelwe izolo ngo-<xliff:g id="TIME_DATE">%1$s</xliff:g>"</string>
+ <string name="wear_app_perms_7d_access" msgid="4608069019194676432">"Kufinyelelwe ngo-<xliff:g id="TIME_DATE_0">%1$s</xliff:g> <xliff:g id="TIME_DATE_1">%2$s</xliff:g>"</string>
+ <string name="test_otp_msg" msgid="7559110574222727550">"Iphasiwedi yakho yesikhathi esisodwa ithi 132435"</string>
+ <string name="enhanced_confirmation_dialog_title" msgid="7562437438040966351">"Amasethingi akhawulelwe"</string>
+ <string name="enhanced_confirmation_dialog_desc" msgid="5921240234843839219">"Ukuze uphephe, leli sethingi okwamanje alitholakali."</string>
+ <string name="enhanced_confirmation_dialog_title_permission" msgid="2149144789394238266">"I-app inqatshelwe ukufinyelela ku-<xliff:g id="PERMISSION_NAME">%1$s</xliff:g>"</string>
+ <string name="enhanced_confirmation_dialog_desc_permission" msgid="3150778951946468945">"I-app icele ukufinyelela emvumweni ezwelayo okungabeka imininingwane yakho siqu neyezimali engozini.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kungenzeka ukuthi i-app ingasebenzi kahle ngaphandle kwemvume enomkhawulo. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Funda ukuthi ungakuvumela kanjani ukufinyelela&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_role" msgid="1737023798483772780">"I-app iye yanqatshelwa ukufinyelela ukuze ibe yi-<xliff:g id="ROLE_NAME">%1$s</xliff:g> ezenzakalelayo"</string>
+ <string name="enhanced_confirmation_dialog_desc_role" msgid="6369601947905234551">"I-app icele ukufinyelela emvumweni ezwelayo engabeka ulwazi lwakho siqu nolwezimali engozini.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kungenzeka ukuthi i-app ingasebenzi kahle ngaphandle kwelezi zimvume ezinomkhawulo. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Funda ukuthi ungakuvumela kanjani ukufinyelela&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_title_settings_default" msgid="1858092969721041576">"I-app iye yanqatshelwa ukufinyelela"</string>
+ <string name="enhanced_confirmation_dialog_desc_settings_default" msgid="6911632348359332981">"Ukufinyelela kule mvume kungabeka ulwazi lwakho siqu nolwezimali engozini.<xliff:g id="ID_1">&lt;br&gt;&lt;br&gt;</xliff:g>Kungenzeka ukuthi i-app ingasebenzi kahle ngaphandle kwemvume enomkhawulo. &lt;a href=<xliff:g id="LEARN_MORE_LINK">%1$s</xliff:g>&gt;Funda ukuthi ungakuvumela kanjani ukufinyelela&lt;/a&gt;"</string>
+ <string name="enhanced_confirmation_dialog_learn_more" msgid="5226619861379095709">"Funda kabanzi"</string>
+ <string name="enhanced_confirmation_dialog_ok" msgid="8560373821598619924">"KULUNGILE"</string>
+ <string name="permission_grant_dialog_streaming_blocked_title" msgid="8905241017017043649">"Isicelo semvume sicindezelwe"</string>
+ <string name="permission_grant_dialog_streaming_blocked_description" msgid="838165608934085319">"Le-app icela izimvume ezengeziwe, kodwa izimvume azikwazi ukunikezwa ngesikhathi sokusakaza-bukhoma. Nikeza imvume kufoni yakho kuqala."</string>
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label" msgid="5702912511473457693">"Okwekholi yezimo eziphuthumayo noma umyalezo"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_title" msgid="849723944428031911">"Indawo ithunyelwe emasevisini ezimo eziphuthumayo"</string>
+ <string name="privacy_dashboard_emergency_location_dialog_description" msgid="5815970230573483329">"Le app ifinyelele indawo yedivayisi yakho phakathi nekholi noma umyalezo othunyelwe enombolweni yezimo eziphuthumayo. Lokhu kungenzeka lapho i-app ingenayo imvume yendawo noma uma indawo yedivayisi ivaliwe. "<a href="https://support.google.com/android/answer/9319337">"Funda kabanzi"</a></string>
</resources>
diff --git a/PermissionController/res/values/bools.xml b/PermissionController/res/values/bools.xml
index e97761843..b5f33b081 100644
--- a/PermissionController/res/values/bools.xml
+++ b/PermissionController/res/values/bools.xml
@@ -19,4 +19,5 @@
<resources>
<bool name="is_at_least_t">false</bool>
<bool name="is_at_least_u">false</bool>
+ <bool name="is_at_least_v">false</bool>
</resources>
diff --git a/PermissionController/res/values/colors.xml b/PermissionController/res/values/colors.xml
index e54edb506..b1d285104 100644
--- a/PermissionController/res/values/colors.xml
+++ b/PermissionController/res/values/colors.xml
@@ -32,4 +32,7 @@
<!-- overviewBackground is not visible from mainline, so UX provided this alternative.
system_neutral2_200 is v31+ so use this placeholder provided by ux -->
<color name="permission_rationale_overview_background">#DADCE0</color>
+
+ <!-- Wear related colors -->
+ <color name="wear_material_gray_600">#FF80868B</color>
</resources>
diff --git a/PermissionController/res/values/dimens.xml b/PermissionController/res/values/dimens.xml
index f638e89df..53d5341fe 100644
--- a/PermissionController/res/values/dimens.xml
+++ b/PermissionController/res/values/dimens.xml
@@ -77,4 +77,5 @@
<dimen name="privhub_circle_stroke_width">8dp</dimen>
<dimen name="privhub_details_recycler_view_bottom_padding">96dp</dimen>
+
</resources>
diff --git a/PermissionController/res/values/overlayable.xml b/PermissionController/res/values/overlayable.xml
index 850b5df40..0cf52ac4a 100644
--- a/PermissionController/res/values/overlayable.xml
+++ b/PermissionController/res/values/overlayable.xml
@@ -24,6 +24,7 @@
<!-- START HELP LINKS -->
<item type="string" name="help_app_permissions" />
<item type="string" name="data_sharing_help_center_link" />
+ <item type="string" name="help_url_action_disabled_by_restricted_settings" />
<!-- END HELP LINKS -->
<!-- START PERMISSION GRANT DIALOG -->
@@ -37,12 +38,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 +65,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" />
- <!-- END IDs for V31 only -->
+ <!-- Used in V30 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 -->
@@ -332,6 +349,14 @@
<item type="style" name="WarningBannerWidgetFrame" />
<!-- END WARNING BANNER PREFERENCE STYLE -->
+ <!-- START AUTOMOTIVE WARNING BANNER PREFERENCE STYLE -->
+ <item type="style" name="AutoWarningBannerCardView" />
+ <item type="style" name="AutoWarningBannerIcon" />
+ <item type="style" name="AutoWarningBannerText" />
+ <item type="style" name="AutoWarningBannerTitle" />
+ <item type="style" name="AutoWarningBannerSummary" />
+ <!-- END AUTOMOTIVE WARNING BANNER PREFERENCE STYLE -->
+
<!-- START PRIVACY CONTROLS CONFIGS -->
<item type="string" name="camera_toggle_enable_config"/>
<item type="string" name="mic_toggle_enable_config"/>
@@ -362,6 +387,35 @@
<item type="style" name="AppDataSharingUpdateSettingsIcon" />
<!-- END SAFETY LABELS STYLE -->
+ <!--START WEAR SPECIFIC FONT STRINGS -->
+ <item type="string" name="wear_material_compose_display_1_font_family" />
+ <item type="string" name="wear_material_compose_display_2_font_family" />
+ <item type="string" name="wear_material_compose_display_3_font_family" />
+ <item type="string" name="wear_material_compose_title_1_font_family" />
+ <item type="string" name="wear_material_compose_title_2_font_family" />
+ <item type="string" name="wear_material_compose_title_3_font_family" />
+ <item type="string" name="wear_material_compose_body_1_font_family" />
+ <item type="string" name="wear_material_compose_body_2_font_family" />
+ <item type="string" name="wear_material_compose_button_font_family" />
+ <item type="string" name="wear_material_compose_caption_1_font_family" />
+ <item type="string" name="wear_material_compose_caption_2_font_family" />
+ <item type="string" name="wear_material_compose_caption_3_font_family" />
+ <!--END WEAR SPECIFIC FONT STRINGS -->
+
+ <!-- START ENHANCED CONFIRMATION DIALOG -->
+ <item type="style" name="Theme.EnhancedConfirmationDialog" />
+ <item type="style" name="Theme.EnhancedConfirmationDialogFragment" />
+ <item type="style" name="EnhancedConfirmationDialog" />
+ <item type="style" name="EnhancedConfirmationDialogHeader" />
+ <item type="style" name="EnhancedConfirmationDialogIcon" />
+ <item type="style" name="EnhancedConfirmationDialogTitle" />
+ <item type="style" name="EnhancedConfirmationDialogScrollView" />
+ <item type="style" name="EnhancedConfirmationDialogBody" />
+ <item type="style" name="EnhancedConfirmationDialogDesc" />
+ <item type="style" name="EnhancedConfirmationDialogButtonBar" />
+ <item type="style" name="EnhancedConfirmationDialogButton" />
+ <!-- END ENHANCED CONFIRMATION DIALOG -->
+
</policy>
</overlayable>
@@ -526,4 +580,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..e114d69bc 100644
--- a/PermissionController/res/values/strings.xml
+++ b/PermissionController/res/values/strings.xml
@@ -30,6 +30,9 @@
<!-- Button label for back action [CHAR LIMIT=20] -->
<string name="back">Back</string>
+ <!-- Button label for closing a dialog action -->
+ <string name="dialog_close">Close</string>
+
<!-- Label indicating access is allowed. If you update this string, please
update the string "quick_settings_camera_mic_available" in System Ui [CHAR LIMIT=NONE] -->
<string name="available">Available</string>
@@ -73,6 +76,9 @@
<!-- 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>
+
<!-- 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 +170,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>
@@ -330,11 +339,6 @@
<!-- Title of the permission dialog for accessibility purposes- spoken to the user. [CHAR LIMIT=none] -->
<string name="permission_request_title">Permission request</string>
- <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=30] -->
- <string name="wear_not_allowed_dlg_title">Android Wear</string>
- <!-- Title of dialog telling users that Install/Uninstall action is not supported on Android Wear. [CHAR LIMIT=none] -->
- <string name="wear_not_allowed_dlg_text">Install/Uninstall actions not supported on Wear.</string>
-
<!-- Review of runtime permissions for legacy apps -->
<!-- Template for the screen title when app permissions are reviewed on install. [CHAR LIMIT=none] -->
@@ -580,30 +584,33 @@
<!-- Summary for an app's use of a permission without duration [CHAR LIMIT=none] -->
<string name="app_permission_usage_summary_no_duration">Access: <xliff:g id="num" example="2">%1$s</xliff:g> times. Last used <xliff:g id="time" example="2 hours">%2$s</xliff:g> ago.</string>
- <!-- Title for the dialog button to allow a permission grant when you cannot only allow in the foreground. [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to allow a permission grant when you cannot only allow in the foreground. [CHAR LIMIT=60] -->
<string name="app_permission_button_allow">Allow</string>
- <!-- Title for the dialog button to allow a storage permission grant for all files [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to allow a storage permission grant for all files [CHAR LIMIT=60] -->
<string name="app_permission_button_allow_all_files">Allow management of all files</string>
- <!-- Title for the dialog button to allow a storage permission grant for media files only [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to allow a storage permission grant for media files only [CHAR LIMIT=60] -->
<string name="app_permission_button_allow_media_only">Allow access to media only</string>
- <!-- Title for the dialog button to allow a permission grant when you can also only allow in the foreground. [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to allow a permission grant when you can also only allow in the foreground. [CHAR LIMIT=60] -->
<string name="app_permission_button_allow_always">Allow all the time</string>
- <!-- Title for the dialog button to allow a permission grant only when the app is in the foreground. [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to allow a permission grant only when the app is in the foreground. [CHAR LIMIT=60] -->
<string name="app_permission_button_allow_foreground">Allow only while using the app</string>
- <!-- Title for the dialog button to allow the user to always allow access to all resources guarded by a permission. [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to allow the user to always allow access to all resources guarded by a permission. [CHAR LIMIT=60] -->
<string name="app_permission_button_always_allow_all">Always allow all</string>
- <!-- Title for the dialog button to require an app to ask for a permission next time they need it. [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to require an app to ask for a permission next time they need it. [CHAR LIMIT=60] -->
<string name="app_permission_button_ask">Ask every time</string>
- <!-- Title for the dialog button to deny with prejudice a permission grant. [CHAR LIMIT=60] -->
+ <!-- Title for the settings radio button to deny with prejudice a permission grant. [CHAR LIMIT=60] -->
<string name="app_permission_button_deny">Don\u2019t allow</string>
+ <!-- Title for the settings radio button to allow limited access. [CHAR LIMIT=60] -->
+ <string name="app_permission_button_allow_limited_access">Allow limited access</string>
+
<!-- Content description for precise location image. [CHAR LIMIT=50] -->
<string name="precise_image_description">Precise location</string>
@@ -622,6 +629,9 @@
<!-- Description for showing an app's permission [CHAR LIMIT=60] -->
<string name="app_permission_header"><xliff:g id="perm" example="location">%1$s</xliff:g> access for this app</string>
+ <!-- Description for showing an app's permission along with device name [CHAR LIMIT=NONE] -->
+ <string name="app_permission_header_with_device_name"><xliff:g id="perm" example="camera">%1$s</xliff:g> access for this app on <xliff:g id="device_name" example="My Laptop">%2$s</xliff:g></string>
+
<!-- Text for linking to the page that shows an app's permissions [CHAR LIMIT=none] -->
<string name="app_permission_footer_app_permissions_link">See all <xliff:g id="app" example="Maps">%1$s</xliff:g> permissions</string>
@@ -643,9 +653,15 @@
<!-- Label for the hibernation / auto revoke switch on T+ devices [CHAR LIMIT=40] -->
<string name="unused_apps_label_v2">Pause app activity if unused</string>
+ <!-- Label of a switch preference that controls whether the system will pause app activity when the app has not been used for a while [CHAR LIMIT=40]-->
+ <string name="unused_apps_label_v3">Manage app if unused</string>
+
<!-- Hibernation switch preference summary which describes what the toggle does on T+ devices [CHAR LIMIT=NONE] -->
<string name="unused_apps_summary">Remove permissions, delete temporary files, and stop notifications</string>
+ <!-- Summary of the switch preference that controls whether the system will pause app activity when the app has not been used for a while [CHAR LIMIT=NONE]-->
+ <string name="unused_apps_summary_v2">Remove permissions, delete temporary files, stop notifications, and archive the app</string>
+
<!-- Summary for stating that permissions will be removed [CHAR LIMIT=none] -->
<string name="auto_revoke_summary">To protect your data, permissions for this app will be removed if the app is unused for a few months.</string>
@@ -791,6 +807,9 @@
<!-- Header for denied permissions/apps [CHAR LIMIT=40] -->
<string name="denied_header">Not allowed</string>
+ <!-- Header to display the Permission group name along with corresponding device name [CHAR LIMIT=None] -->
+ <string name="permission_group_name_with_device_name"><xliff:g id="perm_group_name" example="camera">%1$s</xliff:g> on <xliff:g id="device_name" example="My Laptop">%2$s</xliff:g></string>
+
<!-- Text of hyperlink shown in storage_footer [CHAR LIMIT=60] -->
<string name="storage_footer_hyperlink_text">See more apps that can access all files</string>
@@ -1215,6 +1234,15 @@
<!-- Search keywords for the NOTES role. [CHAR LIMIT=NONE] -->
<string name="role_notes_search_keywords">notes</string>
+ <!-- Label for the wallet role. [CHAR LIMIT=30] -->
+ <string name="role_wallet_label">Default wallet app</string>
+ <!-- Short label for the wallet role. [CHAR LIMIT=30] -->
+ <string name="role_wallet_short_label">Wallet app</string>
+ <!-- Description for the wallet role. [CHAR LIMIT=NONE] -->
+ <string name="role_wallet_description">Wallet apps can store your credit and loyalty cards, car keys and other things to help with various forms of transactions.</string>
+ <string name="role_wallet_request_title">Set <xliff:g id="app_name" example="Super Wallet">%1$s</xliff:g> as your default wallet app?</string>
+ <string name="role_wallet_request_description">No permissions needed</string>
+
<!-- Subtitle for the application that is the current default application [CHAR LIMIT=30] -->
<string name="request_role_current_default">Current default</string>
@@ -1289,6 +1317,9 @@
<!-- Title for category of default apps for work [CHAR LIMIT=30] -->
<string name="default_apps_for_work">Default for work</string>
+ <!-- Title for category of default apps for private profile [CHAR LIMIT=50] -->
+ <string name="default_apps_for_private_profile">Default for private space</string>
+
<!-- Summary of a default app when there is no app set [CHAR LIMIT=60] -->
<string name="default_app_none">None</string>
@@ -1390,36 +1421,59 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<!-- Message shown to the user when the apps requests permission from this group. Shows in the isolated storage case. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_storage_isolated">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos and media on your device?</string>
+ <!-- Message shown to the user when the apps requests permission from this group. Shows in the isolated storage case. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_storage_isolated">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos and media on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_contacts">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your contacts?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_contacts">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your contacts on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_location">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\u2019s location?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_location">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>\u2019s&lt;/b> location?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the location only while app is in foreground [CHAR LIMIT=150]-->
<string name="permgrouprequestdetail_location">The app will only have access to the location while you\u2019re using the app</string>
<!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupbackgroundrequest_location">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\u2019s location?</string>
+ <!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupbackgroundrequest_device_aware_location">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>\u2019s&lt;/b> location?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupbackgroundrequestdetail_location">This app may want to access your location all the time, even when you\u2019re not using the app. <annotation id="link">Allow in settings.</annotation></string>
<!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupupgraderequest_location">Change location access for &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b>?</string>
+ <!-- Message shown to the user when the apps requests permission to use the location while app is in foreground and background on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupupgraderequest_device_aware_location">Change location access for &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the location while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupupgraderequestdetail_location">This app wants to access your location all the time, even when you\u2019re not using the app. <annotation id="link">Allow in settings.</annotation></string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_nearby_devices">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to find, connect to, and determine the relative position of nearby devices?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_nearby_devices">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to find, connect to, and determine the relative position of nearby devices on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to discovery and connect to nearby devices while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupupgraderequestdetail_nearby_devices">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to find, connect to, and determine the relative position of nearby devices? <annotation id="link">Allow in settings.</annotation></string>
<!-- Message shown to the user when the app requests permission to upgrade to fine location [CHAR LIMIT=120] -->
<string name="permgrouprequest_fineupgrade">Change <xliff:g id="app_name" example="Gmail">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\u2019s location access from approximate to precise?</string>
+ <!-- Message shown to the user when the app requests permission to upgrade to fine location on a specific device [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_fineupgrade">Change <xliff:g id="app_name" example="Gmail">&lt;b&gt;%1$s&lt;/b&gt;</xliff:g>\u2019s location access on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b> from approximate to precise?</string>
<!-- Message shown to the user when the app requests permission to use coarse location [CHAR LIMIT=120] -->
<string name="permgrouprequest_coarselocation">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access this device\u2019s approximate location?</string>
+ <!-- Message shown to the user when the app requests permission to use coarse location for a specific device [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_coarselocation">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>\u2019s approximate location?</string>
<!-- Text for the FINE location image [CHAR LIMIT=20] -->
<string name="permgrouprequest_finelocation_imagetext">Precise</string>
<!-- Text for the COARSE location image [CHAR LIMIT=20] -->
@@ -1429,14 +1483,26 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<string name="permgrouprequest_calendar">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your calendar?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_calendar">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your calendar on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_sms">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send and view SMS messages?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_sms">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send and view SMS messages on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_storage">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos, media, and files on your device?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_storage">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos, media, and files on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_storage_q_to_s">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access &lt;b>photos, videos, music, and audio&lt;/b> on this device?</string>
@@ -1448,25 +1514,42 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_read_media_aural">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access music and audio on this device?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_read_media_aural">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access music and audio on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_read_media_visual">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos and videos on this device?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_read_media_visual">Allow &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access photos and videos on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_more_photos">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access more photos and videos on this device?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_more_photos">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access more photos and videos on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_microphone">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_microphone">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the microphone only while app is in foreground [CHAR LIMIT=150]-->
<string name="permgrouprequestdetail_microphone">The app will only be able to record audio while you\u2019re using the app</string>
<!-- Message shown to the user when the apps requests permission to use the microphone while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupbackgroundrequest_microphone">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio?</string>
+ <!-- Message shown to the user when the apps requests permission to use the microphone while app is in foreground and background. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupbackgroundrequest_device_aware_microphone">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to record audio on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the microphone while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupbackgroundrequestdetail_microphone">This app may want to record audio all the time, even when you\u2019re not using the app. <annotation id="link">Allow in settings.</annotation></string>
<!-- Message shown to the user when the apps requests permission to use the microphone while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupupgraderequest_microphone">Change microphone access for &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b>?</string>
+ <!-- Message shown to the user when the apps requests permission to use the microphone while app is in foreground and background. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupupgraderequest_device_aware_microphone">Change microphone access for &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the microphone while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupupgraderequestdetail_microphone">This app wants to record audio all the time, even when you\u2019re not using the app. <annotation id="link">Allow in settings.</annotation></string>
@@ -1474,46 +1557,76 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<string name="permgrouprequest_activityRecognition">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your physical activity?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_activityRecognition">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your physical activity on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
+
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_camera">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video?</string>
+
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_camera">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the camera only while app is in foreground [CHAR LIMIT=150]-->
<string name="permgrouprequestdetail_camera">The app will only be able to take pictures and record video while you\u2019re using the app</string>
<!-- Message shown to the user when the apps requests permission to use the camera while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupbackgroundrequest_camera">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video?</string>
+ <!-- Message shown to the user when the apps requests permission to use the camera while app is in foreground and background on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupbackgroundrequest_device_aware_camera">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to take pictures and record video on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the camera while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupbackgroundrequestdetail_camera">This app may want to take pictures and record video all the time, even when you\u2019re not using the app. <annotation id="link">Allow in settings.</annotation></string>
<!-- Message shown to the user when the apps requests permission to use the camera while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupupgraderequest_camera">Change camera access for &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b>?</string>
+ <!-- Message shown to the user when the apps requests permission to use the camera while app is in foreground and background on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupupgraderequest_device_aware_camera">Change camera access for &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the camera while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=150] -->
<string name="permgroupupgraderequestdetail_camera">This app wants to take pictures and record video all the time, even when you\u2019re not using the app. <annotation id="link">Allow in settings.</annotation></string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_calllog">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your phone call logs?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_calllog">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access your phone call logs on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_phone">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to make and manage phone calls?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_phone">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to make and manage phone calls on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_sensors">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access sensor data about your vital signs?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_sensors">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access sensor data about your vital signs on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the body sensors while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=NONE] -->
<string name="permgroupupgraderequestdetail_sensors">This app wants to access sensor data about your vital signs all the time, even when you\u2019re not using the app. To make this change, <annotation id="link">go to settings.</annotation></string>
- <!-- Message shown to the user when the apps requests permission to use the bosy sensors while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
+ <!-- Message shown to the user when the apps requests permission to use the bosy sensors while app is in foreground and background. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=120] -->
<string name="permgroupbackgroundrequest_sensors">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access the sensor data about your vital signs?</string>
+ <!-- Message shown to the user when the apps requests permission to use the bosy sensors while app is in foreground and background on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupbackgroundrequest_device_aware_sensors">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access the sensor data about your vital signs on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Subtitle of the message shown to the user when the apps requests permission to use the body sensors while app is in foreground and background. Try to keep the link annotation at the end of the string [CHAR LIMIT=NONE] -->
<string name="permgroupbackgroundrequestdetail_sensors">To let this app access body sensor data all the time, even when you\u2019re not using the app, <annotation id="link">go to settings.</annotation></string>
<!-- Message shown to the user when the apps requests permission to use the body sensors while app is in foreground and background. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgroupupgraderequest_sensors">Keep allowing &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access body sensor data while app is in use?</string>
+ <!-- Message shown to the user when the apps requests permission to use the body sensors while app is in foreground and background on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgroupupgraderequest_device_aware_sensors">Keep allowing &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to access body sensor data on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b> while app is in use?</string>
<!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] -->
<string name="permgrouprequest_notifications">Allow
&lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send you notifications?</string>
+ <!-- Message shown to the user when the apps requests permission from this group on a specific device. If ever possible this should stay below 120 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 180 characters though. [CHAR LIMIT=180] -->
+ <string name="permgrouprequest_device_aware_notifications">Allow
+ &lt;b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g>&lt;/b> to send you notifications on &lt;b><xliff:g id="device_name" example="My Laptop">%2$s</xliff:g>&lt;/b>?</string>
<!-- Notification shown to the user when location permissions are auto-granted by admin policy. These are for when the admin is forcing the permission and the user cannot control it. [CHAR LIMIT=120]-->
<string name="auto_granted_permissions">Controlled permissions</string>
@@ -1549,6 +1662,8 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<string name="privdash_label_24h">Past\n24 hours</string>
<!-- Label that describes a "past 7 days" time window, prefer two lines. [CHAR LIMIT=20] -->
<string name="privdash_label_7d">Past\n7 days</string>
+ <!-- This information will aid accessibility applications in describing permission usage percentages to users. While the chart visually represents permission usage, accessibility tools will convey the specific percentages for those who need auditory assistance. Examples would be "Microphone 40 percent" or "Location 75 percent" [CHAR LIMIT=none] -->
+ <string name="privdash_usage_percent"><xliff:g id="permission_name" example="camera">%1$s</xliff:g> <xliff:g id="percent" example="25">%2$d</xliff:g> percent</string>
<!-- Info label for status bar indicator permissions (Mic and Camera) for apps holding special exempted roles. [CHAR LIMIT=none] -->
<string name="exempt_mic_camera_info_label"><xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is protected by Android. Because your data is processed on this device, this app’s permission usage isn’t shown on the status bar or your privacy dashboard. </string>
@@ -1567,6 +1682,20 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<string name="blocked_mic_summary">Microphone data may still be shared when you call an emergency number.</string>
<!-- Label for the button to change the sensor status [CHAR LIMIT=none] -->
<string name="blocked_sensor_button_label">Change</string>
+ <!-- Info label for the warning banner title if automotive camera access is off[CHAR LIMIT=none] -->
+ <string name="automotive_blocked_camera_title">Camera access is off</string>
+ <!-- Info label for the warning banner title if automotive microphone access is off[CHAR LIMIT=none] -->
+ <string name="automotive_blocked_microphone_title">Microphone access is off</string>
+ <!-- Info label for the warning banner title if automotive location access is off[CHAR LIMIT=none] -->
+ <string name="automotive_blocked_location_title">Location access is off</string>
+ <!-- Info label to display that the sensor is blocked for automotive infotainment apps and services [CHAR LIMIT=none] -->
+ <string name="automotive_blocked_infotainment_app_summary">For infotainment apps</string>
+ <!-- Info label to display that the sensor is blocked for automotive required apps and services [CHAR LIMIT=none] -->
+ <string name="automotive_blocked_required_app_summary">For required apps</string>
+ <!-- Info label for the required app title which needs to access sensors such as camera, location and microphone [CHAR LIMIT=NONE]-->
+ <string name="automotive_required_app_title">This app is required</string>
+ <!-- Info label to display when the automotive app is a required app for accessing sensors such as camera, location and microphone [CHAR LIMIT=NONE]-->
+ <string name="automotive_required_app_summary">This app is required by your car\u2019s manufacturer</string>
<!-- Title for page containing overall view of device safety status [CHAR LIMIT=30] -->
<string name="safety_center_dashboard_page_title">Security &amp; privacy</string>
@@ -1850,4 +1979,59 @@ Allow <xliff:g id="app_name" example="Gmail">%4$s</xliff:g> to upload a bug repo
<string name="safety_label_changes_gear_description">Settings</string>
<!-- Safety Label Change Notifications End -->
+
+ <!-- Summary for showing the last access text for today for Wear [CHAR LIMIT=50] -->
+ <string name="wear_app_perms_24h_access">Accessed <xliff:g id="time_date" example="12:42 PM">%1$s</xliff:g></string>
+
+ <!-- Summary for showing the last access text for yesterday for Wear [CHAR LIMIT=50] -->
+ <string name="wear_app_perms_24h_access_yest">Accessed yesterday <xliff:g id="time_date" example="12:42 PM">%1$s</xliff:g></string>
+
+ <!-- Summary for showing the last access text for 7 days for Wear [CHAR LIMIT=50] -->
+ <string name="wear_app_perms_7d_access">Accessed <xliff:g id="time_date" example="Jan 3">%1$s</xliff:g> <xliff:g id="time_date" example="12:42 PM">%2$s</xliff:g></string>
+
+ <!-- A string representing a message (sms, email, etc.) telling the user about a one time password. Used for testing [CHAR LIMIT=NONE] -->
+ <string name="test_otp_msg">Your one time password is 132435</string>
+
+ <!-- START ENHANCED CONFIRMATION DIALOG -->
+
+ <!--Title for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=50] -->
+ <string name="enhanced_confirmation_dialog_title">Restricted setting</string>
+ <!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_dialog_desc">For your security, this setting is currently unavailable.</string>
+
+ <!--Title for dialog displayed to tell user that permissions are blocked by setting restrictions [CHAR LIMIT=50] -->
+ <string name="enhanced_confirmation_dialog_title_permission">App was denied access to <xliff:g id="permission_name" example="contacts">%1$s</xliff:g></string>
+ <!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_dialog_desc_permission">The app requested access to a sensitive permission which can put your personal and financial info at risk.<xliff:g>&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=&quot;<xliff:g id="learn_more_link">%1$s</xliff:g>&quot;&gt;Learn how to allow access&lt;/a&gt;</string>
+
+ <string name="enhanced_confirmation_dialog_title_role">App was denied access to be default <xliff:g id="role_name" example="phone app">%1$s</xliff:g></string>
+ <!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_dialog_desc_role">The app requested access to sensitive permissions which can put your personal and financial info at risk.<xliff:g>&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without these restricted permissions. &lt;a href=&quot;<xliff:g id="learn_more_link">%1$s</xliff:g>&quot;&gt;Learn how to allow access&lt;/a&gt;</string>
+
+ <string name="enhanced_confirmation_dialog_title_settings_default">App was denied access</string>
+ <!--Content for dialog displayed to tell user that settings are blocked by setting restrictions [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_dialog_desc_settings_default">Access to this permission can put your personal and financial info at risk.<xliff:g>&lt;br&gt;&lt;br&gt;</xliff:g>It\'s possible the app won\'t work properly without this restricted permission. &lt;a href=&quot;<xliff:g id="learn_more_link">%1$s</xliff:g>&quot;&gt;Learn how to allow access&lt;/a&gt;</string>
+
+ <!-- Button label to allow the user to view additional information [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_dialog_learn_more">Learn more</string>
+ <!-- Button label of the ok button [CHAR LIMIT=NONE] -->
+ <string name="enhanced_confirmation_dialog_ok">OK</string>
+ <!-- Help URI, action disabled by restricted settings [DO NOT TRANSLATE] -->
+ <string name="help_url_action_disabled_by_restricted_settings" translatable="false"></string>
+
+ <!-- END ENHANCED CONFIRMATION DIALOG -->
+
+ <!-- Title for the warning dialog that shows when permission grant dialog is blocked from streaming to a remote device [CHAR LIMIT=70]-->
+ <string name="permission_grant_dialog_streaming_blocked_title">Permission request suppressed</string>
+
+ <!-- Descriptions for the warning dialog that shows when permission grant dialog is blocked from streaming to a remote device [CHAR LIMIT=200] -->
+ <string name="permission_grant_dialog_streaming_blocked_description">This app is requesting additional permissions, but permissions can’t be granted in a streaming session. Grant the permission on your phone first.</string>
+
+ <!-- Attribution label that is enforced to show for emergency location accesses in the privacy dashboard -->
+ <string name="privacy_dashboard_emergency_location_enforced_attribution_label">For emergency call or text</string>
+ <!-- Title of the dialog when clicking the app op access entry for EMERGENCY_LOCATION in the privacy dashboard -->
+ <string name="privacy_dashboard_emergency_location_dialog_title">Location sent to emergency services</string>
+ <!-- Description of the dialog when clicking the app op access entry for EMERGENCY_LOCATION in the privacy dashboard -->
+ <string name="privacy_dashboard_emergency_location_dialog_description">This app accessed your device\'s location during a call or text to an emergency number. This can happen even when the app doesn\'t have location permission or the device location is off. <a href="https://support.google.com/android/answer/9319337">Learn more</a></string>
+ <!-- Close button in the pop up dialog when clicking the app op access entry in the privacy dashboard -->
</resources>
diff --git a/PermissionController/res/values/styles.xml b/PermissionController/res/values/styles.xml
index 11b9ebec1..043934ac4 100644
--- a/PermissionController/res/values/styles.xml
+++ b/PermissionController/res/values/styles.xml
@@ -51,7 +51,7 @@
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:orientation">vertical</item>
- <item name="android:paddingTop">18dp</item>
+ <item name="android:paddingTop">24dp</item>
<item name="android:paddingBottom">24dp</item>
<item name="android:paddingLeft">24dp</item>
<item name="android:paddingRight">24dp</item>
@@ -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 -->
@@ -570,6 +568,7 @@
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:textDirection">locale</item>
</style>
@@ -1040,6 +1039,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 -->
@@ -1296,6 +1296,111 @@
<item name="android:orientation">vertical</item>
</style>
+ <style name="AutoWarningBannerCardView">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="cardCornerRadius">20dp</item>
+ <item name="cardBackgroundColor">@color/warning_surface</item>
+ <item name="cardElevation">0dp</item>
+ <item name="contentPaddingBottom">24dp</item>
+ <item name="contentPaddingTop">24dp</item>
+ <item name="contentPaddingLeft">24dp</item>
+ <item name="contentPaddingRight">24dp</item>
+ </style>
+
+ <style name="AutoWarningBannerIcon">
+ <item name="android:layout_width">44dp</item>
+ <item name="android:layout_height">44dp</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:layout_marginBottom">8dp</item>
+ <item name="android:tint">@color/warning_onsurface</item>
+ </style>
+
+ <style name="AutoWarningBannerText">
+ <item name="android:layout_marginBottom">4dp</item>
+ <item name="android:textColor">@android:color/black</item>
+ </style>
+
+ <style name="AutoWarningBannerTitle" parent="@style/AutoWarningBannerText">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textSize">32sp</item>
+ <item name="android:lineHeight">44sp</item>
+ </style>
+
+ <style name="AutoWarningBannerSummary" parent="@style/AutoWarningBannerText">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:lineHeight">36sp</item>
+ </style>
<!-- END WARNING BANNER -->
+ <!-- START ENHANCED CONFIRMATION DIALOG -->
+
+ <style name="EnhancedConfirmationDialog">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:paddingTop">24dp</item>
+ <item name="android:paddingStart">24dp</item>
+ <item name="android:paddingEnd">24dp</item>
+ <item name="android:paddingBottom">16dp</item>
+ </style>
+
+ <style name="EnhancedConfirmationDialogHeader">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:gravity">center_horizontal</item>
+ <item name="android:paddingBottom">16dp</item>
+ </style>
+
+ <style name="EnhancedConfirmationDialogIcon">
+ <item name="android:src">@drawable/ic_safety_center_shield</item>
+ <item name="android:layout_width">32dp</item>
+ <item name="android:layout_height">32dp</item>
+ <item name="android:scaleType">fitCenter</item>
+ <item name="android:tint">?android:attr/colorAccent</item>
+ <item name="android:contentDescription">@null</item>
+ </style>
+
+ <style name="EnhancedConfirmationDialogTitle" parent="@android:style/TextAppearance.Material.Headline">
+ <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:gravity">center_horizontal</item>
+ </style>
+
+ <style name="EnhancedConfirmationDialogScrollView">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:fadeScrollbars">false</item>
+ </style>
+ <style name="EnhancedConfirmationDialogBody">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:orientation">vertical</item>
+ </style>
+ <style name="EnhancedConfirmationDialogDesc" parent="@android:style/TextAppearance.Material.Body1">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">left</item>
+ <item name="android:fontFamily">google-sans-text</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineSpacingMultiplier">1.2</item>
+ </style>
+
+ <style name="EnhancedConfirmationDialogButtonBar">
+ <item name="android:paddingBottom">14dp</item>
+ <item name="android:paddingTop">12dp</item>
+ </style>
+
+ <style name="EnhancedConfirmationDialogButton">
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineSpacingMultiplier">1.25</item>
+ </style>
+
+ <!-- END ENHANCED CONFIRMATION DIALOG -->
</resources>
diff --git a/PermissionController/res/values/themes.xml b/PermissionController/res/values/themes.xml
index 76196a050..2e6af4af1 100644
--- a/PermissionController/res/values/themes.xml
+++ b/PermissionController/res/values/themes.xml
@@ -107,6 +107,11 @@
<style name="Theme.DeviceDefault.Dialog.NoActionBar.DayNight" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar" />
+ <!--
+ TODO(b/309578419): Make activities handle insets properly and then remove this.
+ -->
+ <style name="OptOutEdgeToEdgeEnforcement" />
+
<!-- Do not allow OEMs to overlay these themes.
Must Guarantee that filterTouches is set for these activities -->
@@ -138,4 +143,27 @@
<item name="android:filterTouchesWhenObscured">true</item>
</style>
+ <style name="Theme.EnhancedConfirmationDialog.FilterTouches" parent="Theme.EnhancedConfirmationDialog">
+ <item name="android:filterTouchesWhenObscured">true</item>
+ </style>
+
+ <style name="Theme.PermissionController.IncidentReportDialog"
+ parent="@style/Theme.DeviceDefault.Dialog.NoActionBar.DayNight" />
+
+ <!-- START ENHANCED CONFIRMATION DIALOG -->
+
+ <style name="Theme.EnhancedConfirmationDialog" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="windowNoTitle">true</item>
+ <item name="android:alertDialogTheme">@style/Theme.EnhancedConfirmationDialogFragment</item>
+ <item name="android:buttonBarStyle">@style/EnhancedConfirmationDialogButtonBar</item>
+ <item name="android:buttonBarPositiveButtonStyle">@style/EnhancedConfirmationDialogButton</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/EnhancedConfirmationDialogButton</item>
+ <item name="android:buttonBarNeutralButtonStyle">@style/EnhancedConfirmationDialogButton</item>
+ </style>
+
+ <style name="Theme.EnhancedConfirmationDialogFragment" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+ <item name="android:buttonBarStyle">@style/EnhancedConfirmationDialogButtonBar</item>
+ </style>
+
+ <!-- END ENHANCED CONFIRMATION DIALOG -->
</resources>
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml
index 2f8f5a291..6e65b96e3 100644
--- a/PermissionController/res/xml/roles.xml
+++ b/PermissionController/res/xml/roles.xml
@@ -78,6 +78,7 @@
<permission name="android.permission.READ_MEDIA_AUDIO" minSdkVersion="33" />
<permission name="android.permission.READ_MEDIA_VIDEO" minSdkVersion="33" />
<permission name="android.permission.READ_MEDIA_IMAGES" minSdkVersion="33" />
+ <permission name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" minSdkVersion="34" />
</permission-set>
<permission-set name="nearby_devices">
@@ -140,6 +141,10 @@
minSdkVersion="33" />
<permission name="android.permission.EXECUTE_APP_ACTION"
minSdkVersion="34" />
+ <permission name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
+ minSdkVersion="35" optionalMinSdkVersion="34" />
+ <permission name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"
+ minSdkVersion="35" />
</permissions>
<app-op-permissions>
<app-op-permission name="android.permission.SYSTEM_ALERT_WINDOW" />
@@ -163,8 +168,7 @@
overrideUserWhenGranting="true"
requestDescription="@string/role_browser_request_description"
requestTitle="@string/role_browser_request_title"
- shortLabel="@string/role_browser_short_label"
- uiBehavior="BrowserRoleUiBehavior">
+ shortLabel="@string/role_browser_short_label">
<!--
~ Required components matching is handled in BrowserRoleBehavior because it needs the
~ PackageManager.MATCH_ALL flag and other manual filtering, which cannot fit in our
@@ -444,8 +448,13 @@
</preferred-activity>
</preferred-activities>
<permissions>
- <permission name="android.permission.READ_HOME_APP_SEARCH_DATA" minSdkVersion="33"/>
+ <permission name="android.permission.READ_HOME_APP_SEARCH_DATA" minSdkVersion="33" />
+ <permission name="android.permission.ALLOW_SLIPPERY_TOUCHES" minSdkVersion="33" optionalMinSdkVersion="30" />
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35" />
</permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<!--- @see android.telecom.CallRedirectionService -->
@@ -516,7 +525,7 @@
<role
name="android.app.role.SYSTEM_AUTOMOTIVE_CLUSTER"
- behavior="AutomotiveRoleBehavior"
+ behavior="v31.AutomotiveRoleBehavior"
defaultHolders="config_systemAutomotiveCluster"
exclusive="true"
minSdkVersion="31"
@@ -533,7 +542,7 @@
<role
name="android.app.role.COMPANION_DEVICE_WATCH"
- behavior="CompanionDeviceWatchRoleBehavior"
+ behavior="v31.CompanionDeviceWatchRoleBehavior"
description="@string/role_watch_description"
exclusive="false"
minSdkVersion="31"
@@ -545,18 +554,25 @@
<permission-set name="sms" />
<permission-set name="contacts" />
<permission-set name="nearby_devices" />
+ <permission-set name="notifications" minSdkVersion="35" />
+ <!-- If this role holder has a NotificationListenerService, let that service receive
+ notifications with sensitive content unredacted-->
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35"/>
</permissions>
<app-op-permissions>
<app-op-permission name="android.permission.MANAGE_ONGOING_CALLS" />
<app-op-permission name="android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER" />
+ <app-op-permission name="android.permission.MEDIA_ROUTING_CONTROL" minSdkVersion="35" />
</app-op-permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<role
name="android.app.role.SYSTEM_AUTOMOTIVE_PROJECTION"
- allowBypassingQualification="true"
defaultHolders="config_systemAutomotiveProjection"
- exclusive="false"
+ exclusive="true"
minSdkVersion="31"
static="true"
systemOnly="true"
@@ -579,12 +595,19 @@
<permission name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION" minSdkVersion="33" />
<permission name="android.permission.ADD_TRUSTED_DISPLAY" minSdkVersion="34"/>
<permission name="android.permission.ASSOCIATE_COMPANION_DEVICES" minSdkVersion="34"/>
+ <!-- If this role holder has a NotificationListenerService, let that service receive
+ notifications with sensitive content unredacted-->
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35"/>
+ <permission name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" minSdkVersion="35" />
</permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<role
name="android.app.role.SYSTEM_SHELL"
- behavior="SystemShellRoleBehavior"
+ behavior="v31.SystemShellRoleBehavior"
defaultHolders="config_systemShell"
exclusive="true"
minSdkVersion="31"
@@ -629,6 +652,8 @@
<permission name="android.permission.MANAGE_DEVICE_POLICY_CALLS" minSdkVersion="34" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_CAMERA"
minSdkVersion="34" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"
+ minSdkVersion="35" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
minSdkVersion="34" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET"
@@ -667,6 +692,11 @@
<permission name="android.permission.SET_TIME" minSdkVersion="34" />
<permission name="android.permission.SET_TIME_ZONE" minSdkVersion="34" />
<permission name="android.permission.SATELLITE_COMMUNICATION" minSdkVersion="34" />
+ <permission name="android.permission.ALWAYS_UPDATE_WALLPAPER" minSdkVersion="35" />
+ <permission name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"
+ minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"
+ minSdkVersion="35" />
</permissions>
</role>
@@ -750,7 +780,7 @@
-->
<role
name="android.app.role.SYSTEM_TELEVISION_NOTIFICATION_HANDLER"
- behavior="TelevisionRoleBehavior"
+ behavior="v31.TelevisionRoleBehavior"
defaultHolders="config_systemTelevisionNotificationHandler"
exclusive="true"
minSdkVersion="31"
@@ -959,7 +989,13 @@
<permission-set name="notifications" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
<permission name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
+ <!-- If this role holder has a NotificationListenerService, let that service receive
+ notifications with sensitive content unredacted-->
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35"/>
</permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<!---
@@ -1050,7 +1086,7 @@
-->
<role
name="android.app.role.SYSTEM_DOCUMENT_MANAGER"
- behavior="DocumentManagerRoleBehavior"
+ behavior="v33.DocumentManagerRoleBehavior"
exclusive="true"
minSdkVersion="33"
static="true"
@@ -1101,7 +1137,6 @@
-->
<role
name="android.app.role.SYSTEM_UI"
- behavior="SystemUiRoleBehavior"
defaultHolders="config_systemUi"
exclusive="true"
minSdkVersion="31"
@@ -1113,7 +1148,13 @@
<permission name="android.permission.MANAGE_SENSOR_PRIVACY" />
<permission name="android.permission.OBSERVE_SENSOR_PRIVACY" />
<permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" minSdkVersion="33"/>
+ <!-- If this role holder has a NotificationListenerService, let that service receive
+ notifications with sensitive content unredacted-->
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35"/>
</permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<!---
@@ -1121,7 +1162,7 @@
-->
<role
name="android.app.role.SYSTEM_TELEVISION_REMOTE_SERVICE"
- behavior="TelevisionRoleBehavior"
+ behavior="v31.TelevisionRoleBehavior"
defaultHolders="config_systemTelevisionRemoteService"
exclusive="true"
minSdkVersion="31"
@@ -1140,7 +1181,7 @@
<role
name="android.app.role.COMPANION_DEVICE_APP_STREAMING"
allowBypassingQualification="true"
- behavior="CompanionDeviceAppStreamingRoleBehavior"
+ behavior="v33.CompanionDeviceAppStreamingRoleBehavior"
description="@string/role_app_streaming_description"
exclusive="false"
minSdkVersion="33"
@@ -1166,7 +1207,7 @@
<role
name="android.app.role.COMPANION_DEVICE_COMPUTER"
allowBypassingQualification="true"
- behavior="CompanionDeviceComputerRoleBehavior"
+ behavior="v33.CompanionDeviceComputerRoleBehavior"
description="@string/role_companion_device_computer_description"
exclusive="false"
minSdkVersion="33"
@@ -1175,12 +1216,18 @@
<permissions>
<permission-set name="notifications" />
<permission-set name="storage" />
+ <!-- If this role holder has a NotificationListenerService, let that service receive
+ notifications with sensitive content unredacted-->
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35"/>
</permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<role
name="android.app.role.COMPANION_DEVICE_GLASSES"
- behavior="CompanionDeviceGlassesRoleBehavior"
+ behavior="v34.CompanionDeviceGlassesRoleBehavior"
exclusive="false"
minSdkVersion="34"
systemOnly="false"
@@ -1192,10 +1239,16 @@
<permission-set name="notifications" />
<permission-set name="phone" />
<permission-set name="sms" />
+ <!-- If this role holder has a NotificationListenerService, let that service receive
+ notifications with sensitive content unredacted-->
+ <permission name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" minSdkVersion="35"/>
</permissions>
<app-op-permissions>
<app-op-permission name="android.permission.MANAGE_ONGOING_CALLS" />
</app-op-permissions>
+ <app-ops>
+ <app-op name="android:receive_sensitive_notifications" mode="allowed" minSdkVersion="35"/>
+ </app-ops>
</role>
<role
@@ -1236,7 +1289,7 @@
-->
<role
name="android.app.role.DEVICE_POLICY_MANAGEMENT"
- behavior="DevicePolicyManagementRoleBehavior"
+ behavior="v33.DevicePolicyManagementRoleBehavior"
defaultHolders="config_devicePolicyManagement"
exclusive="true"
minSdkVersion="33"
@@ -1329,6 +1382,21 @@
<permission name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER" minSdkVersion="34" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL" minSdkVersion="34" />
<permission name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS" minSdkVersion="34" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE" minSdkVersion="35" />
+ <permission name="android.permission.QUERY_DEVICE_STOLEN_STATE" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_VPN" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCATION" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_LOCALE" minSdkVersion="35" />
+ <permission name="android.permission.MANAGE_DEVICE_POLICY_SMS" minSdkVersion="35" />
</permissions>
</role>
@@ -1343,7 +1411,16 @@
<permissions>
<permission-set name="notifications" />
<permission name="android.permission.GET_HISTORICAL_APP_OPS_STATS" />
+ <permission name="android.permission.READ_SMS" />
+ <permission name="android.permission.RECEIVE_SMS" />
+ <permission name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" minSdkVersion="35" />
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+ <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
+ <permission name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
</permissions>
+ <app-op-permissions>
+ <app-op-permission name="android.permission.SYSTEM_ALERT_WINDOW" />
+ </app-op-permissions>
</role>
<!---
@@ -1352,7 +1429,7 @@
-->
<role
name="android.app.role.SYSTEM_AUTOMOTIVE_CALENDAR_SYNC_MANAGER"
- behavior="AutomotiveRoleBehavior"
+ behavior="v31.AutomotiveRoleBehavior"
defaultHolders="config_systemAutomotiveCalendarSyncManager"
exclusive="true"
minSdkVersion="33"
@@ -1372,7 +1449,7 @@
-->
<role
name="android.app.role.AUTOMOTIVE_NAVIGATION"
- behavior="AutomotiveRoleBehavior"
+ behavior="v31.AutomotiveRoleBehavior"
defaultHolders="config_defaultAutomotiveNavigation"
description="@string/role_automotive_navigation_description"
exclusive="true"
@@ -1529,7 +1606,7 @@
-->
<role
name="android.app.role.SYSTEM_WEAR_HEALTH_SERVICE"
- behavior="SystemWearHealthServiceRoleBehavior"
+ behavior="v33.SystemWearHealthServiceRoleBehavior"
defaultHolders="config_systemWearHealthService"
exclusive="true"
minSdkVersion="33"
@@ -1549,7 +1626,7 @@
-->
<role
name="android.app.role.NOTES"
- behavior="NotesRoleBehavior"
+ behavior="v34.NotesRoleBehavior"
defaultHolders="config_defaultNotes"
description="@string/role_notes_description"
exclusive="true"
@@ -1610,4 +1687,47 @@
</service>
</required-components>
</role>
+
+ <role
+ name="android.app.role.RETAIL_DEMO"
+ behavior="v35.RetailDemoRoleBehavior"
+ defaultHolders="config_defaultRetailDemo"
+ exclusive="true"
+ minSdkVersion="35"
+ static="true"
+ visible="false">
+ <permissions>
+ <permission name="android.permission.ACCESS_BLOBS_ACROSS_USERS" />
+ <permission name="android.permission.CHANGE_CONFIGURATION" />
+ <permission name="android.permission.MODIFY_DAY_NIGHT_MODE" />
+ <permission name="android.permission.MODIFY_PHONE_STATE" />
+ <permission name="android.permission.OBSERVE_APP_USAGE" />
+ <permission name="android.permission.QUERY_USERS" />
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+ <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
+ <permission name="android.permission.WRITE_SETTINGS" />
+ </permissions>
+ <app-op-permissions>
+ <app-op-permission name="android.permission.PACKAGE_USAGE_STATS" />
+ </app-op-permissions>
+ </role>
+
+ <role
+ name="android.app.role.WALLET"
+ behavior="v35.WalletRoleBehavior"
+ defaultHolders="config_defaultWallet"
+ description="@string/role_wallet_description"
+ exclusive="true"
+ label="@string/role_wallet_label"
+ minSdkVersion="35"
+ overrideUserWhenGranting="true"
+ requestable="true"
+ requestDescription="@string/role_wallet_request_description"
+ requestTitle="@string/role_wallet_request_title"
+ showNone="true"
+ shortLabel="@string/role_wallet_short_label"
+ uiBehavior="v35.WalletRoleUiBehavior"/>
+
+
</roles>
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/Android.bp b/PermissionController/role-controller/Android.bp
index 1a790b730..ea6545bb1 100644
--- a/PermissionController/role-controller/Android.bp
+++ b/PermissionController/role-controller/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -26,6 +27,8 @@ java_library {
],
static_libs: [
"modules-utils-build_system",
+ "android.permission.flags-aconfig-java-export",
+ "android.os.flags-aconfig-java-export",
],
apex_available: [
"com.android.permission",
@@ -34,4 +37,7 @@ java_library {
installable: false,
min_sdk_version: "30",
sdk_version: "system_current",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java
index 12710cfd3..c20734cde 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java
@@ -20,12 +20,12 @@ import android.app.ActivityManager;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.os.Process;
import android.os.UserHandle;
import android.service.voice.VoiceInteractionService;
import android.util.ArraySet;
@@ -38,6 +38,7 @@ import androidx.annotation.Nullable;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.model.VisibilityMixin;
import com.android.role.controller.util.UserUtils;
import org.xmlpull.v1.XmlPullParserException;
@@ -54,20 +55,17 @@ public class AssistantRoleBehavior implements RoleBehavior {
private static final String LOG_TAG = AssistantRoleBehavior.class.getSimpleName();
- private static final Intent ASSIST_SERVICE_PROBE =
- new Intent(VoiceInteractionService.SERVICE_INTERFACE);
- private static final Intent ASSIST_ACTIVITY_PROBE = new Intent(Intent.ACTION_ASSIST);
-
@Override
- public void onRoleAdded(@NonNull Role role, @NonNull Context context) {
+ public void onRoleAddedAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
PackageManager packageManager = context.getPackageManager();
if (packageManager.isDeviceUpgrading()) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
- List<String> packageNames = roleManager.getRoleHolders(role.getName());
+ List<String> packageNames = roleManager.getRoleHoldersAsUser(role.getName(), user);
if (packageNames.isEmpty()) {
// If the device was upgraded, and there isn't any legacy role holders, it means
// user selected "None" in Settings and we need to keep that.
- role.onNoneHolderSelectedAsUser(Process.myUserHandle(), context);
+ role.onNoneHolderSelectedAsUser(user, context);
}
}
}
@@ -82,81 +80,72 @@ public class AssistantRoleBehavior implements RoleBehavior {
@Override
public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {
+ return getQualifyingPackagesInternal(null, user, context);
+ }
+
+ @Nullable
+ @Override
+ public Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return !getQualifyingPackagesInternal(packageName, user, context)
+ .isEmpty();
+ }
+
+ @NonNull
+ private List<String> getQualifyingPackagesInternal(@Nullable String filterPackageName,
+ @NonNull UserHandle user, @NonNull Context context) {
Context userContext = UserUtils.getUserContext(context, user);
ActivityManager userActivityManager = userContext.getSystemService(ActivityManager.class);
PackageManager userPackageManager = userContext.getPackageManager();
- Set<String> availableAssistants = new ArraySet<>();
+ Set<String> packageNames = new ArraySet<>();
if (!userActivityManager.isLowRamDevice()) {
- List<ResolveInfo> services = userPackageManager.queryIntentServices(
- ASSIST_SERVICE_PROBE, PackageManager.GET_META_DATA
+ Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+ if (filterPackageName != null) {
+ serviceIntent.setPackage(filterPackageName);
+ }
+ List<ResolveInfo> serviceResolveInfos = userPackageManager.queryIntentServices(
+ serviceIntent, PackageManager.GET_META_DATA
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- int numServices = services.size();
- for (int i = 0; i < numServices; i++) {
- ResolveInfo service = services.get(i);
-
- if (isAssistantVoiceInteractionService(userPackageManager, service.serviceInfo)) {
- availableAssistants.add(service.serviceInfo.packageName);
+ int serviceResolveInfosSize = serviceResolveInfos.size();
+ for (int i = 0; i < serviceResolveInfosSize; i++) {
+ ResolveInfo serviceResolveInfo = serviceResolveInfos.get(i);
+
+ ServiceInfo serviceInfo = serviceResolveInfo.serviceInfo;
+ if (!isAssistantVoiceInteractionService(userPackageManager, serviceInfo)) {
+ if (filterPackageName != null) {
+ Log.w(LOG_TAG, "Package " + filterPackageName
+ + " has an unqualified voice interaction service");
+ }
+ continue;
}
+
+ packageNames.add(serviceInfo.packageName);
}
}
- List<ResolveInfo> activities = userPackageManager.queryIntentActivities(
- ASSIST_ACTIVITY_PROBE, PackageManager.MATCH_DEFAULT_ONLY
+ Intent activityIntent = new Intent(Intent.ACTION_ASSIST);
+ if (filterPackageName != null) {
+ activityIntent.setPackage(filterPackageName);
+ }
+ List<ResolveInfo> activityResolveInfos = userPackageManager.queryIntentActivities(
+ activityIntent, PackageManager.MATCH_DEFAULT_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- int numActivities = activities.size();
- for (int i = 0; i < numActivities; i++) {
- availableAssistants.add(activities.get(i).activityInfo.packageName);
- }
-
- return new ArrayList<>(availableAssistants);
- }
-
- @Nullable
- @Override
- public Boolean isPackageQualified(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {
- ActivityManager activityManager = context.getSystemService(ActivityManager.class);
- PackageManager packageManager = context.getPackageManager();
-
- boolean hasAssistantService = false;
- if (!activityManager.isLowRamDevice()) {
- Intent pkgServiceProbe = new Intent(ASSIST_SERVICE_PROBE).setPackage(packageName);
- List<ResolveInfo> services = packageManager.queryIntentServices(pkgServiceProbe,
- PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- hasAssistantService = !services.isEmpty();
- int numServices = services.size();
- for (int i = 0; i < numServices; i++) {
- ResolveInfo service = services.get(i);
+ int activityResolveInfosSize = activityResolveInfos.size();
+ for (int i = 0; i < activityResolveInfosSize; i++) {
+ ResolveInfo activityResolveInfo = activityResolveInfos.get(i);
- if (isAssistantVoiceInteractionService(packageManager, service.serviceInfo)) {
- return true;
- }
+ ActivityInfo activityInfo = activityResolveInfo.activityInfo;
+ if (!activityInfo.exported) {
+ continue;
}
- }
- Intent pkgActivityProbe = new Intent(ASSIST_ACTIVITY_PROBE).setPackage(packageName);
- boolean hasAssistantActivity = !packageManager.queryIntentActivities(pkgActivityProbe,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE).isEmpty();
- if (!hasAssistantActivity) {
- Log.w(LOG_TAG, "Package " + packageName + " not qualified for " + role.getName()
- + " due to " + (hasAssistantService ? "unqualified" : "missing")
- + " service and missing activity");
+ packageNames.add(activityInfo.packageName);
}
- return hasAssistantActivity;
- }
-
- @Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- }
-
- @Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
+ return new ArrayList<>(packageNames);
}
private boolean isAssistantVoiceInteractionService(@NonNull PackageManager pm,
@@ -205,4 +194,10 @@ public class AssistantRoleBehavior implements RoleBehavior {
return true;
}
+
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return VisibilityMixin.isVisible("config_showDefaultAssistant", false, user, context);
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java
index 2cf6a0b14..0261e1eee 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java
@@ -21,7 +21,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -32,6 +31,7 @@ import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.model.Permissions;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.model.VisibilityMixin;
import com.android.role.controller.util.CollectionUtils;
import com.android.role.controller.util.PackageUtils;
import com.android.role.controller.util.UserUtils;
@@ -61,10 +61,10 @@ public class BrowserRoleBehavior implements RoleBehavior {
@Nullable
@Override
- public String getFallbackHolder(@NonNull Role role, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
- List<String> qualifyingPackageNames = getQualifyingPackagesAsUserInternal(null, false, user,
- context);
+ public String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ List<String> qualifyingPackageNames = getQualifyingPackagesAsUserInternal(null, false,
+ user, context);
if (qualifyingPackageNames.size() == 1) {
return qualifyingPackageNames.get(0);
}
@@ -76,7 +76,7 @@ public class BrowserRoleBehavior implements RoleBehavior {
return qualifyingSystemPackageNames.get(0);
}
- List<String> defaultPackageNames = role.getDefaultHolders(context);
+ List<String> defaultPackageNames = role.getDefaultHoldersAsUser(user, context);
return CollectionUtils.firstOrNull(defaultPackageNames);
} else {
return null;
@@ -95,10 +95,10 @@ public class BrowserRoleBehavior implements RoleBehavior {
@Nullable
@Override
- public Boolean isPackageQualified(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {
+ public Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
List<String> packageNames = getQualifyingPackagesAsUserInternal(packageName, false,
- Process.myUserHandle(), context);
+ user, context);
return !packageNames.isEmpty();
}
@@ -133,24 +133,32 @@ public class BrowserRoleBehavior implements RoleBehavior {
}
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
// @see com.android.server.pm.permission.DefaultPermissionGrantPolicy
// #grantDefaultPermissionsToDefaultBrowser(java.lang.String, int)
if (SdkLevel.isAtLeastS()) {
- if (PackageUtils.isSystemPackage(packageName, context)) {
- Permissions.grant(packageName, SYSTEM_BROWSER_PERMISSIONS, false, false, true,
- false, false, context);
+ if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) {
+ Permissions.grantAsUser(packageName, SYSTEM_BROWSER_PERMISSIONS, false, false,
+ true, false, false, user, context);
}
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (SdkLevel.isAtLeastT()) {
- if (PackageUtils.isSystemPackage(packageName, context)) {
- Permissions.revoke(packageName, SYSTEM_BROWSER_PERMISSIONS, true, false, false,
- context);
+ if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) {
+ Permissions.revokeAsUser(packageName, SYSTEM_BROWSER_PERMISSIONS, true, false,
+ false, user, context);
}
}
}
+
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return VisibilityMixin.isVisible("config_showBrowserRole", true, user, context);
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java
index 79c139cee..153f4a6b4 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java
@@ -26,7 +26,9 @@ import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.model.Permissions;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.model.VisibilityMixin;
import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.UserUtils;
import java.util.Arrays;
import java.util.List;
@@ -50,25 +52,36 @@ public class DialerRoleBehavior implements RoleBehavior {
@Override
public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {
+ if (UserUtils.isPrivateProfile(user, context)) {
+ return false;
+ }
TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
return telephonyManager.isVoiceCapable();
}
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (SdkLevel.isAtLeastS()) {
- if (PackageUtils.isSystemPackage(packageName, context)) {
- Permissions.grant(packageName, SYSTEM_DIALER_PERMISSIONS, false, false,
- true, false, false, context);
+ if (PackageUtils.isSystemPackageAsUser(packageName, user, context)) {
+ Permissions.grantAsUser(packageName, SYSTEM_DIALER_PERMISSIONS, false, false,
+ true, false, false, user, context);
}
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (SdkLevel.isAtLeastS()) {
- Permissions.revoke(packageName, SYSTEM_DIALER_PERMISSIONS, true, false, false, context);
+ Permissions.revokeAsUser(packageName, SYSTEM_DIALER_PERMISSIONS, true, false, false,
+ user, context);
}
}
+
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return VisibilityMixin.isVisible("config_showDialerRole", true, user, context);
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java
index a54006bb5..f19c86596 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java
@@ -18,7 +18,6 @@ package com.android.role.controller.behavior;
import android.content.Context;
import android.content.pm.PackageInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
@@ -27,6 +26,7 @@ import androidx.annotation.Nullable;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.model.VisibilityMixin;
import com.android.role.controller.util.PackageUtils;
import java.util.List;
@@ -49,15 +49,16 @@ public class EmergencyRoleBehavior implements RoleBehavior {
@Nullable
@Override
- public String getFallbackHolder(@NonNull Role role, @NonNull Context context) {
- List<String> packageNames = role.getQualifyingPackagesAsUser(Process.myUserHandle(),
- context);
+ public String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ List<String> packageNames = role.getQualifyingPackagesAsUser(user, context);
PackageInfo fallbackPackageInfo = null;
int packageNamesSize = packageNames.size();
for (int i = 0; i < packageNamesSize; i++) {
String packageName = packageNames.get(i);
- PackageInfo packageInfo = PackageUtils.getPackageInfo(packageName, 0, context);
+ PackageInfo packageInfo = PackageUtils.getPackageInfoAsUser(packageName, 0,
+ user, context);
if (packageInfo == null) {
continue;
}
@@ -68,4 +69,10 @@ public class EmergencyRoleBehavior implements RoleBehavior {
}
return fallbackPackageInfo != null ? fallbackPackageInfo.packageName : null;
}
+
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return VisibilityMixin.isVisible("config_showDefaultEmergency", false, user, context);
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java
index 3254bc6e4..8c1446b50 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java
@@ -20,17 +20,21 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
+import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.role.controller.model.AppOpPermissions;
import com.android.role.controller.model.Permissions;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.model.VisibilityMixin;
import com.android.role.controller.util.UserUtils;
import java.util.Arrays;
@@ -51,6 +55,18 @@ public class HomeRoleBehavior implements RoleBehavior {
android.Manifest.permission.WRITE_CALL_LOG,
android.Manifest.permission.READ_CONTACTS);
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static final List<String> WEAR_PERMISSIONS_T = Arrays.asList(
+ android.Manifest.permission.POST_NOTIFICATIONS,
+ android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY);
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final List<String> WEAR_PERMISSIONS_V = Arrays.asList(
+ android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER);
+
+ private static final List<String> WEAR_APP_OP_PERMISSIONS = Arrays.asList(
+ android.Manifest.permission.SYSTEM_ALERT_WINDOW);
+
@Override
public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {
@@ -62,10 +78,12 @@ public class HomeRoleBehavior implements RoleBehavior {
*/
@Nullable
@Override
- public String getFallbackHolder(@NonNull Role role, @NonNull Context context) {
- PackageManager packageManager = context.getPackageManager();
+ public String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
Intent intent = role.getRequiredComponents().get(0).getIntentFilterData().createIntent();
- List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent,
+ List<ResolveInfo> resolveInfos = userPackageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
@@ -78,7 +96,8 @@ public class HomeRoleBehavior implements RoleBehavior {
// Leave the fallback to PackageManagerService if there is only the fallback home in
// Settings, because if we fallback to it here, we cannot fallback to a normal home
// later, and user cannot see the fallback home in the UI anyway.
- if (isSettingsApplication(resolveInfo.activityInfo.applicationInfo, context)) {
+ if (isSettingsApplicationAsUser(resolveInfo.activityInfo.applicationInfo, user,
+ context)) {
continue;
}
if (resolveInfo.priority > priority) {
@@ -94,14 +113,16 @@ public class HomeRoleBehavior implements RoleBehavior {
/**
* Check if the application is a settings application
*/
- public static boolean isSettingsApplication(@NonNull ApplicationInfo applicationInfo,
- @NonNull Context context) {
- PackageManager packageManager = context.getPackageManager();
- ResolveInfo resolveInfo = packageManager.resolveActivity(new Intent(
- Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY
+ private static boolean isSettingsApplicationAsUser(@NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ ResolveInfo resolveInfo = userPackageManager.resolveActivity(
+ new Intent(Settings.ACTION_SETTINGS), PackageManager.MATCH_DEFAULT_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- if (resolveInfo == null || resolveInfo.activityInfo == null) {
+ if (resolveInfo == null || resolveInfo.activityInfo == null
+ || !resolveInfo.activityInfo.exported) {
return false;
}
return Objects.equals(applicationInfo.packageName, resolveInfo.activityInfo.packageName);
@@ -119,46 +140,61 @@ public class HomeRoleBehavior implements RoleBehavior {
}
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- Permissions.grant(packageName, AUTOMOTIVE_PERMISSIONS,
- true, false, true, false, false, context);
+ Permissions.grantAsUser(packageName, AUTOMOTIVE_PERMISSIONS,
+ true, false, true, false, false, user, context);
}
- // Before T, ALLOW_SLIPPERY_TOUCHES may either not exist, or may not be a role permission
- if (isRolePermission(android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, context)) {
- Permissions.grant(packageName,
- Arrays.asList(android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES),
- true, false, true, false, false, context);
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (SdkLevel.isAtLeastT()) {
+ Permissions.grantAsUser(packageName, WEAR_PERMISSIONS_T,
+ true, false, true, false, false, user, context);
+ for (String permission : WEAR_APP_OP_PERMISSIONS) {
+ AppOpPermissions.grantAsUser(packageName, permission, true, user, context);
+ }
+ }
+ if (SdkLevel.isAtLeastV()) {
+ Permissions.grantAsUser(packageName, WEAR_PERMISSIONS_V,
+ true, false, true, false, false, user, context);
+ }
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- Permissions.revoke(packageName, AUTOMOTIVE_PERMISSIONS, true, false, false, context);
+ Permissions.revokeAsUser(packageName, AUTOMOTIVE_PERMISSIONS, true, false, false,
+ user, context);
}
- // Before T, ALLOW_SLIPPERY_TOUCHES may either not exist, or may not be a role permission
- if (isRolePermission(android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, context)) {
- Permissions.revoke(packageName,
- Arrays.asList(android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES),
- true, false, false, context);
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ if (SdkLevel.isAtLeastT()) {
+ Permissions.revokeAsUser(packageName, WEAR_PERMISSIONS_T, true, false, false,
+ user, context);
+ for (String permission : WEAR_APP_OP_PERMISSIONS) {
+ AppOpPermissions.revokeAsUser(packageName, permission, user, context);
+ }
+ }
+ if (SdkLevel.isAtLeastV()) {
+ Permissions.revokeAsUser(packageName, WEAR_PERMISSIONS_V, true, false, false,
+ user, context);
+ }
}
}
- /**
- * Return true if the permission exists, and has 'role' protection level.
- * Return false otherwise.
- */
- private boolean isRolePermission(@NonNull String permissionName, @NonNull Context context) {
- PermissionInfo permissionInfo;
- try {
- permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
- final int flags = permissionInfo.getProtectionFlags();
- return (flags & PermissionInfo.PROTECTION_FLAG_ROLE) == PermissionInfo.PROTECTION_FLAG_ROLE;
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return VisibilityMixin.isVisible("config_showDefaultHome", false, user, context);
+ }
+
+ @Override
+ public boolean isApplicationVisibleAsUser(@NonNull Role role,
+ @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return !isSettingsApplicationAsUser(applicationInfo, user, context);
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java
index c1186a930..4aff8a163 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java
@@ -19,7 +19,6 @@ package com.android.role.controller.behavior;
import android.app.admin.DevicePolicyManager;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.TelephonyManager;
@@ -31,6 +30,7 @@ import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.model.Permissions;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.model.VisibilityMixin;
import com.android.role.controller.util.CollectionUtils;
import com.android.role.controller.util.PackageUtils;
import com.android.role.controller.util.UserUtils;
@@ -59,7 +59,8 @@ public class SmsRoleBehavior implements RoleBehavior {
public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {
if (SdkLevel.isAtLeastU()) {
- if (UserUtils.isCloneProfile(user, context)) {
+ if (UserUtils.isCloneProfile(user, context)
+ || UserUtils.isPrivateProfile(user, context)) {
return false;
}
@@ -90,7 +91,7 @@ public class SmsRoleBehavior implements RoleBehavior {
TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
if (!telephonyManager.isSmsCapable()
// Ensure sms role is present on car despite !isSmsCapable config (b/132972702)
- && role.getDefaultHolders(context).isEmpty()) {
+ && role.getDefaultHoldersAsUser(user, context).isEmpty()) {
return false;
}
return true;
@@ -98,8 +99,9 @@ public class SmsRoleBehavior implements RoleBehavior {
@Nullable
@Override
- public String getFallbackHolder(@NonNull Role role, @NonNull Context context) {
- List<String> defaultPackageNames = role.getDefaultHolders(context);
+ public String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ List<String> defaultPackageNames = role.getDefaultHoldersAsUser(user, context);
if (!defaultPackageNames.isEmpty()) {
return defaultPackageNames.get(0);
}
@@ -107,24 +109,32 @@ public class SmsRoleBehavior implements RoleBehavior {
// TODO(b/132916161): This was the previous behavior, however this may allow any third-party
// app to suddenly become the default SMS app and get the permissions, if no system default
// SMS app is available.
- List<String> qualifyingPackageNames = role.getQualifyingPackagesAsUser(
- Process.myUserHandle(), context);
+ List<String> qualifyingPackageNames = role.getQualifyingPackagesAsUser(user, context);
return CollectionUtils.firstOrNull(qualifyingPackageNames);
}
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- if (SdkLevel.isAtLeastS() && PackageUtils.isSystemPackage(packageName, context)) {
- Permissions.grant(packageName, SYSTEM_SMS_PERMISSIONS, false, false,
- true, false, false, context);
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ if (SdkLevel.isAtLeastS() && PackageUtils.isSystemPackageAsUser(packageName, user,
+ context)) {
+ Permissions.grantAsUser(packageName, SYSTEM_SMS_PERMISSIONS, false, false, true,
+ false, false, user, context);
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (SdkLevel.isAtLeastS()) {
- Permissions.revoke(packageName, SYSTEM_SMS_PERMISSIONS, true, false, false, context);
+ Permissions.revokeAsUser(packageName, SYSTEM_SMS_PERMISSIONS, true, false, false,
+ user, context);
}
}
+
+ @Override
+ public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return VisibilityMixin.isVisible("config_showSmsRole", true, user, context);
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemUiRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemUiRoleBehavior.java
deleted file mode 100644
index 210c2313f..000000000
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemUiRoleBehavior.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role.controller.behavior;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-import androidx.annotation.NonNull;
-
-import com.android.modules.utils.build.SdkLevel;
-import com.android.role.controller.model.AppOpPermissions;
-import com.android.role.controller.model.Role;
-import com.android.role.controller.model.RoleBehavior;
-
-import java.util.Arrays;
-import java.util.List;
-
-/** The role behavior for system ui. */
-public class SystemUiRoleBehavior implements RoleBehavior {
-
- private static final List<String> WEAR_APP_OP_PERMISSIONS =
- Arrays.asList(android.Manifest.permission.SYSTEM_ALERT_WINDOW);
-
- @Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- if (SdkLevel.isAtLeastT()) {
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- for (String permission : WEAR_APP_OP_PERMISSIONS) {
- AppOpPermissions.grant(packageName, permission, true, context);
- }
- }
- }
- }
-
- @Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- if (SdkLevel.isAtLeastT()) {
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- for (String permission : WEAR_APP_OP_PERMISSIONS) {
- AppOpPermissions.revoke(packageName, permission, context);
- }
- }
- }
- }
-}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/AutomotiveRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/AutomotiveRoleBehavior.java
index 6eedb8f4d..022e127e6 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/AutomotiveRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/AutomotiveRoleBehavior.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v31;
import android.content.Context;
import android.content.pm.PackageManager;
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceWatchRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/CompanionDeviceWatchRoleBehavior.java
index 7f5d68b14..b98035fb2 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceWatchRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/CompanionDeviceWatchRoleBehavior.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v31;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -33,18 +32,18 @@ import com.android.role.controller.util.UserUtils;
public class CompanionDeviceWatchRoleBehavior implements RoleBehavior {
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.grantNotificationAccessForPackage(context, packageName);
+ NotificationUtils.grantNotificationAccessForPackageAsUser(packageName, user, context);
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.revokeNotificationAccessForPackage(context, packageName);
+ NotificationUtils.revokeNotificationAccessForPackageAsUser(packageName, user, context);
}
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemShellRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/SystemShellRoleBehavior.java
index c0cbe9c25..8c6eafa26 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemShellRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/SystemShellRoleBehavior.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v31;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -26,6 +26,7 @@ import androidx.annotation.Nullable;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.util.UserUtils;
/**
* Class for behavior of the system shell role.
@@ -33,12 +34,13 @@ import com.android.role.controller.model.RoleBehavior;
public class SystemShellRoleBehavior implements RoleBehavior {
@Nullable
@Override
- public Boolean isPackageQualified(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {
- PackageManager packageManager = context.getPackageManager();
+ public Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
int uid;
try {
- uid = packageManager.getPackageUid(packageName, 0);
+ uid = userPackageManager.getPackageUid(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/TelevisionRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/TelevisionRoleBehavior.java
index 88021e62d..d418ba300 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/TelevisionRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v31/TelevisionRoleBehavior.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v31;
import android.content.Context;
import android.content.pm.PackageManager;
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceAppStreamingRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/CompanionDeviceAppStreamingRoleBehavior.java
index 1c57efd01..275387d57 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceAppStreamingRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/CompanionDeviceAppStreamingRoleBehavior.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v33;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -33,18 +32,18 @@ import com.android.role.controller.util.UserUtils;
public class CompanionDeviceAppStreamingRoleBehavior implements RoleBehavior {
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.grantNotificationAccessForPackage(context, packageName);
+ NotificationUtils.grantNotificationAccessForPackageAsUser(packageName, user, context);
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.revokeNotificationAccessForPackage(context, packageName);
+ NotificationUtils.revokeNotificationAccessForPackageAsUser(packageName, user, context);
}
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceComputerRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/CompanionDeviceComputerRoleBehavior.java
index c5de0d848..9967751ed 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceComputerRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/CompanionDeviceComputerRoleBehavior.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v33;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -33,18 +32,18 @@ import com.android.role.controller.util.UserUtils;
public class CompanionDeviceComputerRoleBehavior implements RoleBehavior {
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.grantNotificationAccessForPackage(context, packageName);
+ NotificationUtils.grantNotificationAccessForPackageAsUser(packageName, user, context);
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.revokeNotificationAccessForPackage(context, packageName);
+ NotificationUtils.revokeNotificationAccessForPackageAsUser(packageName, user, context);
}
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/DevicePolicyManagementRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/DevicePolicyManagementRoleBehavior.java
index 648b7d198..34e7e1843 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/DevicePolicyManagementRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/DevicePolicyManagementRoleBehavior.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v33;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/DocumentManagerRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/DocumentManagerRoleBehavior.java
index ee7984274..3fb7b2c56 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/DocumentManagerRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/DocumentManagerRoleBehavior.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v33;
import android.content.Context;
-import android.os.Process;
+import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -36,9 +36,9 @@ public class DocumentManagerRoleBehavior implements RoleBehavior {
@NonNull
@Override
- public List<String> getDefaultHolders(@NonNull Role role, @NonNull Context context) {
- List<String> qualifyingPackageNames = role.getQualifyingPackagesAsUser(
- Process.myUserHandle(), context);
+ public List<String> getDefaultHoldersAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ List<String> qualifyingPackageNames = role.getQualifyingPackagesAsUser(user, context);
if (qualifyingPackageNames.size() == 1) {
return qualifyingPackageNames;
} else {
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemWearHealthServiceRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/SystemWearHealthServiceRoleBehavior.java
index d9f34291d..446bdac84 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/SystemWearHealthServiceRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v33/SystemWearHealthServiceRoleBehavior.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v33;
import android.content.Context;
import android.content.pm.PackageManager;
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceGlassesRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/CompanionDeviceGlassesRoleBehavior.java
index 5988cd4b6..00d7e5f31 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/CompanionDeviceGlassesRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/CompanionDeviceGlassesRoleBehavior.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v34;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -33,18 +32,18 @@ import com.android.role.controller.util.UserUtils;
public class CompanionDeviceGlassesRoleBehavior implements RoleBehavior {
@Override
- public void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.grantNotificationAccessForPackage(context, packageName);
+ NotificationUtils.grantNotificationAccessForPackageAsUser(packageName, user, context);
}
}
@Override
- public void revoke(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {
- UserHandle user = Process.myUserHandle();
+ public void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
if (!UserUtils.isManagedProfile(user, context)) {
- NotificationUtils.revokeNotificationAccessForPackage(context, packageName);
+ NotificationUtils.revokeNotificationAccessForPackageAsUser(packageName, user, context);
}
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/NotesRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java
index 14c189a0f..71f7795f7 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/behavior/NotesRoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v34/NotesRoleBehavior.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.role.controller.behavior;
+package com.android.role.controller.behavior.v34;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.UserHandle;
@@ -28,6 +29,8 @@ import com.android.role.controller.model.Role;
import com.android.role.controller.model.RoleBehavior;
import com.android.role.controller.util.UserUtils;
+import java.util.Objects;
+
/**
* Class for behavior of the Notes role.
*/
@@ -38,7 +41,7 @@ public class NotesRoleBehavior implements RoleBehavior {
public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {
// Role should be enabled by OEMs.
- Resources resources = context.getResources();
+ Resources resources = getResources(context);
if (!resources.getBoolean(android.R.bool.config_enableDefaultNotes)) {
return false;
}
@@ -55,4 +58,27 @@ public class NotesRoleBehavior implements RoleBehavior {
return true;
}
+
+ /**
+ * Gets {@link Resources} for fetching notes role related resources.
+ * <p>
+ * When running within the system server process, this method retrieves system resource that
+ * include Runtime Resource Overlay (RRO) values. These RRO values are not accessible through
+ * the {@code context} object provided to this class during construction.
+ *
+ * @throws RuntimeException when no system package is found when this class runs in the system
+ * server process.
+ */
+ @NonNull
+ private static Resources getResources(@NonNull Context context) {
+ if (Objects.equals(context.getPackageName(), "android")) {
+ try {
+ return context.getPackageManager().getResourcesForApplication("system");
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("System package not found", e);
+ }
+ } else {
+ return context.getResources();
+ }
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/v35/RetailDemoRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v35/RetailDemoRoleBehavior.java
new file mode 100644
index 000000000..9905ed0ae
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v35/RetailDemoRoleBehavior.java
@@ -0,0 +1,65 @@
+/*
+ * 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.behavior.v35;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.util.UserUtils;
+
+/**
+ * Class for behavior of the Retail Demo role.
+ */
+@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+public class RetailDemoRoleBehavior implements RoleBehavior {
+
+ @Override
+ public Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ if (Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 0) == 0) {
+ return false;
+ }
+ Context userContext = UserUtils.getUserContext(context, user);
+ DevicePolicyManager userDevicePolicyManager =
+ userContext.getSystemService(DevicePolicyManager.class);
+ if (!(userDevicePolicyManager.isDeviceOwnerApp(packageName)
+ || userDevicePolicyManager.isProfileOwnerApp(packageName))) {
+ return false;
+ }
+ // Fallback to do additional default check.
+ return null;
+ }
+
+ @Override
+ public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ UserManager userUserManager = userContext.getSystemService(UserManager.class);
+ return userUserManager.isSystemUser() || userUserManager.isMainUser()
+ || userUserManager.isDemoUser();
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/v35/WalletRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/v35/WalletRoleBehavior.java
new file mode 100644
index 000000000..d01e59456
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/v35/WalletRoleBehavior.java
@@ -0,0 +1,175 @@
+/*
+ * 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.behavior.v35;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.OffHostApduService;
+import android.os.Build;
+import android.os.UserHandle;
+import android.permission.flags.Flags;
+import android.service.quickaccesswallet.QuickAccessWalletService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.util.CollectionUtils;
+import com.android.role.controller.util.UserUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Handles the behavior of the wallet role.
+ */
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+public class WalletRoleBehavior implements RoleBehavior {
+
+ private static final String LOG_TAG = WalletRoleBehavior.class.getSimpleName();
+
+ @Override
+ public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return SdkLevel.isAtLeastV() && Flags.walletRoleEnabled()
+ && !UserUtils.isProfile(user, context);
+ }
+
+ @Nullable
+ @Override
+ public List<String> getDefaultHoldersAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ ComponentName preferredPaymentService =
+ CardEmulation.getPreferredPaymentService(userContext);
+ if (preferredPaymentService != null) {
+ return Collections.singletonList(preferredPaymentService.getPackageName());
+ }
+
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return CollectionUtils.firstOrNull(role.getDefaultHoldersAsUser(user, context));
+ }
+
+ @Nullable
+ @Override
+ public Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return !getQualifyingPackageNamesInternal(packageName, user, context).isEmpty();
+ }
+
+ @Nullable
+ @Override
+ public List<String> getQualifyingPackagesAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return new ArrayList<>(getQualifyingPackageNamesInternal(null, user, context));
+ }
+
+ @NonNull
+ private static Set<String> getQualifyingPackageNamesInternal(@Nullable String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ Set<String> packageNames = resolvePackageNames(QuickAccessWalletService.SERVICE_INTERFACE,
+ packageName, user, context);
+ if (isNfcHostCardEmulationSupported(context)) {
+ packageNames.addAll(getQualifyingApduServicesAsUser(packageName, false, user,
+ context));
+ packageNames.addAll(getQualifyingApduServicesAsUser(packageName, true, user,
+ context));
+ }
+ return packageNames;
+ }
+
+ @NonNull
+ private static Set<String> resolvePackageNames(@NonNull String action,
+ @Nullable String packageName, @NonNull UserHandle user, @NonNull Context context) {
+ Intent intent = new Intent(action).setPackage(packageName);
+ PackageManager packageManager = context.getPackageManager();
+ List<ResolveInfo> resolveInfos = packageManager
+ .queryIntentServicesAsUser(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, user);
+ Set<String> packageNames = new ArraySet<>();
+ int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
+ if (!serviceInfo.exported) {
+ continue;
+ }
+ packageNames.add(serviceInfo.packageName);
+ }
+ return packageNames;
+ }
+
+ @NonNull
+ private static Set<String> getQualifyingApduServicesAsUser(@Nullable String packageName,
+ boolean onHost, @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ Intent intent = new Intent(
+ onHost ? HostApduService.SERVICE_INTERFACE : OffHostApduService.SERVICE_INTERFACE)
+ .setPackage(packageName);
+ List<ResolveInfo> resolveInfos = userPackageManager.queryIntentServices(intent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_META_DATA);
+ Set<String> packageNames = new ArraySet<>();
+ int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ if (!serviceInfo.exported) {
+ continue;
+ }
+ ApduServiceInfo apduServiceInfo;
+ try {
+ apduServiceInfo = new ApduServiceInfo(userPackageManager, resolveInfo, onHost);
+ } catch (IOException | XmlPullParserException e) {
+ Log.w(LOG_TAG, "Unable to create ApduServiceInfo for " + resolveInfo, e);
+ continue;
+ }
+ if (apduServiceInfo.hasCategory(CardEmulation.CATEGORY_PAYMENT)) {
+ packageNames.add(resolveInfo.serviceInfo.packageName);
+ }
+ }
+ return packageNames;
+ }
+
+ private static boolean isNfcHostCardEmulationSupported(@NonNull Context context) {
+ return context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_NFC_HOST_CARD_EMULATION);
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java
index 3efa68bc9..2de1a7b74 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java
@@ -18,11 +18,13 @@ package com.android.role.controller.model;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.os.Process;
+import android.os.Build;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.util.PackageUtils;
import java.util.Objects;
@@ -45,13 +47,20 @@ public class AppOp {
private final Integer mMaxTargetSdkVersion;
/**
+ * The minimum device SDK version for this app op to be granted
+ */
+ private final int mMinSdkVersion;
+
+ /**
* The mode of this app op when granted.
*/
private final int mMode;
- public AppOp(@NonNull String name, @Nullable Integer maxTargetSdkVersion, int mode) {
+ public AppOp(@NonNull String name, @Nullable Integer maxTargetSdkVersion, int minSdkVersion,
+ int mode) {
mName = name;
mMaxTargetSdkVersion = maxTargetSdkVersion;
+ mMinSdkVersion = minSdkVersion;
mMode = mode;
}
@@ -65,6 +74,10 @@ public class AppOp {
return mMaxTargetSdkVersion;
}
+ public int getMinSdkVersion() {
+ return mMinSdkVersion;
+ }
+
public int getMode() {
return mMode;
}
@@ -73,39 +86,53 @@ public class AppOp {
* Grant this app op to an application.
*
* @param packageName the package name of the application
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*
* @return whether any app mode has changed
*/
- public boolean grant(@NonNull String packageName, @NonNull Context context) {
- if (!checkTargetSdkVersion(packageName, context)) {
+ public boolean grantAsUser(@NonNull String packageName, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (!isAvailableAsUser(packageName, user, context)) {
return false;
}
- return Permissions.setAppOpUidMode(packageName, mName, mMode, context);
+ return Permissions.setAppOpUidModeAsUser(packageName, mName, mMode, user, context);
}
/**
* Revoke this app op from an application.
*
* @param packageName the package name of the application
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*
* @return whether any app mode has changed
*/
- public boolean revoke(@NonNull String packageName, @NonNull Context context) {
- if (!checkTargetSdkVersion(packageName, context)) {
+ public boolean revokeAsUser(@NonNull String packageName, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (!isAvailableAsUser(packageName, user, context)) {
return false;
}
int defaultMode = Permissions.getDefaultAppOpMode(mName);
- return Permissions.setAppOpUidMode(packageName, mName, defaultMode, context);
+ return Permissions.setAppOpUidModeAsUser(packageName, mName, defaultMode, user, context);
}
- private boolean checkTargetSdkVersion(@NonNull String packageName, @NonNull Context context) {
+ public boolean isAvailableBySdkVersion() {
+ return Build.VERSION.SDK_INT >= mMinSdkVersion
+ // Workaround to match the value 35 for V in roles.xml before SDK finalization.
+ || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV());
+ }
+
+ private boolean isAvailableAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ if (!isAvailableBySdkVersion()) {
+ return false;
+ }
if (mMaxTargetSdkVersion == null) {
return true;
}
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
+ context);
if (applicationInfo == null) {
return false;
}
@@ -117,6 +144,7 @@ public class AppOp {
return "AppOp{"
+ "mName='" + mName + '\''
+ ", mMaxTargetSdkVersion=" + mMaxTargetSdkVersion
+ + ", mMinSdkVersion=" + mMinSdkVersion
+ ", mMode=" + mMode
+ '}';
}
@@ -130,13 +158,14 @@ public class AppOp {
return false;
}
AppOp appOp = (AppOp) object;
- return mMode == appOp.mMode
+ return mMinSdkVersion == appOp.mMinSdkVersion
+ && mMode == appOp.mMode
&& Objects.equals(mName, appOp.mName)
&& Objects.equals(mMaxTargetSdkVersion, appOp.mMaxTargetSdkVersion);
}
@Override
public int hashCode() {
- return Objects.hash(mName, mMaxTargetSdkVersion, mMode);
+ return Objects.hash(mName, mMaxTargetSdkVersion, mMinSdkVersion, mMode);
}
}
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 29939a1a5..edd74e31e 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
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -42,14 +43,15 @@ public class AppOpPermissions {
* @param appOpPermission the name of the app op permission
* @param overrideNonDefaultMode whether to override the app opp mode if it isn't in the default
* mode
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*
* @return whether any app op mode has changed
*/
- public static boolean grant(@NonNull String packageName, @NonNull String appOpPermission,
- boolean overrideNonDefaultMode, @NonNull Context context) {
- PackageInfo packageInfo = PackageUtils.getPackageInfo(packageName,
- PackageManager.GET_PERMISSIONS, context);
+ public static boolean grantAsUser(@NonNull String packageName, @NonNull String appOpPermission,
+ boolean overrideNonDefaultMode, @NonNull UserHandle user, @NonNull Context context) {
+ PackageInfo packageInfo = PackageUtils.getPackageInfoAsUser(packageName,
+ PackageManager.GET_PERMISSIONS, user, context);
if (packageInfo == null) {
return false;
}
@@ -58,14 +60,16 @@ public class AppOpPermissions {
}
String appOp = AppOpsManagerCompat.permissionToOp(appOpPermission);
if (!overrideNonDefaultMode) {
- Integer currentMode = Permissions.getAppOpMode(packageName, appOp, context);
+ Integer currentMode = Permissions.getAppOpModeAsUser(packageName, appOp, user, context);
if (currentMode != null && currentMode != Permissions.getDefaultAppOpMode(appOp)) {
return false;
}
}
- boolean changed = setAppOpMode(packageName, appOp, AppOpsManager.MODE_ALLOWED, context);
+ boolean changed = setAppOpModeAsUser(packageName, appOp, AppOpsManager.MODE_ALLOWED, user,
+ context);
if (changed) {
- Permissions.setPermissionGrantedByRole(packageName, appOpPermission, true, context);
+ Permissions.setPermissionGrantedByRoleAsUser(packageName, appOpPermission, true,
+ user, context);
}
return changed;
}
@@ -75,24 +79,27 @@ public class AppOpPermissions {
*
* @param packageName the package name of the application
* @param appOpPermission the name of the app op permission
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*
* @return whether any app op mode has changed
*/
- public static boolean revoke(@NonNull String packageName, @NonNull String appOpPermission,
- @NonNull Context context) {
- if (!Permissions.isPermissionGrantedByRole(packageName, appOpPermission, context)) {
+ public static boolean revokeAsUser(@NonNull String packageName, @NonNull String appOpPermission,
+ @NonNull UserHandle user, @NonNull Context context) {
+ if (!Permissions.isPermissionGrantedByRoleAsUser(packageName, appOpPermission, user,
+ context)) {
return false;
}
String appOp = AppOpsManager.permissionToOp(appOpPermission);
int defaultMode = Permissions.getDefaultAppOpMode(appOp);
- boolean changed = setAppOpMode(packageName, appOp, defaultMode, context);
- Permissions.setPermissionGrantedByRole(packageName, appOpPermission, false, context);
+ boolean changed = setAppOpModeAsUser(packageName, appOp, defaultMode, user, context);
+ Permissions.setPermissionGrantedByRoleAsUser(packageName, appOpPermission, false,
+ user, context);
return changed;
}
- private static boolean setAppOpMode(@NonNull String packageName, @NonNull String appOp,
- int mode, @NonNull Context context) {
+ private static boolean setAppOpModeAsUser(@NonNull String packageName, @NonNull String appOp,
+ int mode, @NonNull UserHandle user, @NonNull Context context) {
switch (appOp) {
case AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS:
case AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW:
@@ -105,22 +112,25 @@ public class AppOpPermissions {
case AppOpsManager.OPSTR_MANAGE_IPSEC_TUNNELS:
case AppOpsManager.OPSTR_INSTANT_APP_START_FOREGROUND:
case AppOpsManager.OPSTR_LOADER_USAGE_STATS:
- return Permissions.setAppOpPackageMode(packageName, appOp, mode, context);
+ return Permissions.setAppOpPackageModeAsUser(packageName, appOp, mode, user,
+ context);
case AppOpsManager.OPSTR_INTERACT_ACROSS_PROFILES:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// We fixed OP_INTERACT_ACROSS_PROFILES to use UID mode on S and backported it
// to R, but still, we might have an out-of-date platform or an upgraded
// platform with old state.
boolean changed = false;
- changed |= Permissions.setAppOpUidMode(packageName, appOp, mode, context);
- changed |= Permissions.setAppOpPackageMode(packageName, appOp,
- Permissions.getDefaultAppOpMode(appOp), context);
+ changed |= Permissions.setAppOpUidModeAsUser(packageName, appOp, mode, user,
+ context);
+ changed |= Permissions.setAppOpPackageModeAsUser(packageName, appOp,
+ Permissions.getDefaultAppOpMode(appOp), user, context);
return changed;
} else {
- return Permissions.setAppOpPackageMode(packageName, appOp, mode, context);
+ return Permissions.setAppOpPackageModeAsUser(packageName, appOp, mode, user,
+ context);
}
default:
- return Permissions.setAppOpUidMode(packageName, appOp, mode, context);
+ return Permissions.setAppOpUidModeAsUser(packageName, appOp, mode, user, context);
}
}
}
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..f999e5972 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,11 +16,16 @@
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 android.os.UserHandle;
import androidx.annotation.NonNull;
import com.android.modules.utils.build.SdkLevel;
+import com.android.role.controller.util.UserUtils;
import java.util.Objects;
@@ -40,9 +45,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 +65,56 @@ public class Permission {
return mMinSdkVersion;
}
+ public int getOptionalMinSdkVersion() {
+ return mOptionalMinSdkVersion;
+ }
+
/**
* Check whether this permission is available.
*
+ * @param user the user to check for
+ * @param context the {@code Context} to retrieve system services
+ *
* @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 isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ if (Build.VERSION.SDK_INT >= mMinSdkVersion
+ // Workaround to match the value 35 for V in roles.xml before SDK finalization.
+ || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV())) {
+ return true;
+ }
+ if (Build.VERSION.SDK_INT >= mOptionalMinSdkVersion) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = userPackageManager.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 +122,7 @@ public class Permission {
return "Permission{"
+ "mName='" + mName + '\''
+ ", mMinSdkVersion=" + mMinSdkVersion
+ + ", mOptionalMinSdkVersion=" + mOptionalMinSdkVersion
+ '}';
}
@@ -86,11 +136,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 f55a84c07..e788fdce1 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
@@ -24,7 +24,6 @@ import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.os.Build;
-import android.os.Process;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.util.ArrayMap;
@@ -37,6 +36,7 @@ import androidx.annotation.Nullable;
import com.android.role.controller.util.ArrayUtils;
import com.android.role.controller.util.CollectionUtils;
import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.UserUtils;
import java.util.ArrayList;
import java.util.List;
@@ -61,16 +61,19 @@ public class Permissions {
* Filter a list of permissions based on their SDK versions.
*
* @param permissions the list of permissions
+ * @param user the user to check for
+ * @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> filterBySdkVersionAsUser(@NonNull List<Permission> permissions,
+ @NonNull UserHandle user, @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.isAvailableAsUser(user, context)) {
continue;
}
permissionNames.add(permission.getName());
@@ -89,6 +92,7 @@ public class Permissions {
* @param setGrantedByRole whether the permissions will be granted as granted-by-role
* @param setGrantedByDefault whether the permissions will be granted as granted-by-default
* @param setSystemFixed whether the permissions will be granted as system-fixed
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*
* @return whether any permission or app op changed
@@ -96,16 +100,16 @@ public class Permissions {
* @see com.android.server.pm.permission.DefaultPermissionGrantPolicy#grantRuntimePermissions(
* PackageInfo, java.util.Set, boolean, boolean, int)
*/
- public static boolean grant(@NonNull String packageName, @NonNull List<String> permissions,
- boolean overrideDisabledSystemPackage, boolean overrideUserSetAndFixed,
- boolean setGrantedByRole, boolean setGrantedByDefault, boolean setSystemFixed,
- @NonNull Context context) {
+ public static boolean grantAsUser(@NonNull String packageName,
+ @NonNull List<String> permissions, boolean overrideDisabledSystemPackage,
+ boolean overrideUserSetAndFixed, boolean setGrantedByRole, boolean setGrantedByDefault,
+ boolean setSystemFixed, @NonNull UserHandle user, @NonNull Context context) {
if (setGrantedByRole == setGrantedByDefault) {
throw new IllegalArgumentException("Permission must be either granted by role, or"
+ " granted by default, but not both");
}
- PackageInfo packageInfo = getPackageInfo(packageName, context);
+ PackageInfo packageInfo = getPackageInfoAsUser(packageName, user, context);
if (packageInfo == null) {
return false;
}
@@ -142,7 +146,8 @@ public class Permissions {
// apps, (default grants on first boot and user creation) we don't grant default
// permissions if the version on the system image does not declare them.
if (!overrideDisabledSystemPackage && isUpdatedSystemApp(packageInfo)) {
- PackageInfo disabledSystemPackageInfo = getFactoryPackageInfo(packageName, context);
+ PackageInfo disabledSystemPackageInfo = getFactoryPackageInfoAsUser(packageName, user,
+ context);
if (disabledSystemPackageInfo != null) {
if (ArrayUtils.isEmpty(disabledSystemPackageInfo.requestedPermissions)) {
return false;
@@ -176,9 +181,10 @@ public class Permissions {
boolean permissionOrAppOpChanged = false;
- PackageManager packageManager = context.getPackageManager();
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
Set<String> whitelistedRestrictedPermissions = new ArraySet<>(
- packageManager.getWhitelistedRestrictedPermissions(packageName,
+ userPackageManager.getWhitelistedRestrictedPermissions(packageName,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM));
int sortedPermissionsToGrantLength = sortedPermissionsToGrant.length;
@@ -187,24 +193,26 @@ public class Permissions {
if (isRestrictedPermission(permission, context)
&& whitelistedRestrictedPermissions.add(permission)) {
- packageManager.addWhitelistedRestrictedPermission(packageName, permission,
+ userPackageManager.addWhitelistedRestrictedPermission(packageName, permission,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
}
- permissionOrAppOpChanged |= grantSingle(packageName, permission,
+ permissionOrAppOpChanged |= grantSingleAsUser(packageName, permission,
overrideUserSetAndFixed, setGrantedByRole, setGrantedByDefault, setSystemFixed,
- context);
+ user, context);
}
return permissionOrAppOpChanged;
}
- private static boolean grantSingle(@NonNull String packageName, @NonNull String permission,
- boolean overrideUserSetAndFixed, boolean setGrantedByRole, boolean setGrantedByDefault,
- boolean setSystemFixed, @NonNull Context context) {
- boolean wasPermissionOrAppOpGranted = isPermissionAndAppOpGranted(packageName, permission,
- context);
- if (isPermissionFixed(packageName, permission, false, overrideUserSetAndFixed, context)
+ private static boolean grantSingleAsUser(@NonNull String packageName,
+ @NonNull String permission, boolean overrideUserSetAndFixed, boolean setGrantedByRole,
+ boolean setGrantedByDefault, boolean setSystemFixed, @NonNull UserHandle user,
+ @NonNull Context context) {
+ boolean wasPermissionOrAppOpGranted = isPermissionAndAppOpGrantedAsUser(packageName,
+ permission, user, context);
+ if (isPermissionFixedAsUser(packageName, permission, false,
+ overrideUserSetAndFixed, user, context)
&& !wasPermissionOrAppOpGranted) {
// Stop granting if this permission is fixed to revoked.
return false;
@@ -217,7 +225,8 @@ public class Permissions {
for (int i = 0; i < foregroundPermissionsSize; i++) {
String foregroundPermission = foregroundPermissions.get(i);
- if (isPermissionAndAppOpGranted(packageName, foregroundPermission, context)) {
+ if (isPermissionAndAppOpGrantedAsUser(packageName, foregroundPermission, user,
+ context)) {
isAnyForegroundPermissionGranted = true;
break;
}
@@ -230,8 +239,8 @@ public class Permissions {
}
}
- boolean permissionOrAppOpChanged = grantPermissionAndAppOp(packageName, permission,
- context);
+ boolean permissionOrAppOpChanged = grantPermissionAndAppOpAsUser(packageName, permission,
+ user, context);
// Update permission flags.
int newFlags = 0;
@@ -254,7 +263,7 @@ public class Permissions {
// If a component gets a permission for being the default handler A and also default handler
// B, we grant the weaker grant form. This only applies to default permission grant.
if (setGrantedByDefault && !setSystemFixed) {
- int oldFlags = getPermissionFlags(packageName, permission, context);
+ int oldFlags = getPermissionFlagsAsUser(packageName, permission, user, context);
if ((oldFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
&& (oldFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
if (DEBUG) {
@@ -265,20 +274,22 @@ public class Permissions {
}
}
- setPermissionFlags(packageName, permission, newFlags, newMask, context);
+ setPermissionFlagsAsUser(packageName, permission, newFlags, newMask,
+ user, context);
return permissionOrAppOpChanged;
}
- private static boolean isPermissionAndAppOpGranted(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
+ private static boolean isPermissionAndAppOpGrantedAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
// Check this permission.
- if (!isPermissionGrantedWithoutCheckingAppOp(packageName, permission, context)) {
+ if (!isPermissionGrantedWithoutCheckingAppOpAsUser(packageName, permission, user,
+ context)) {
return false;
}
// Check if the permission is review required.
- if (isPermissionReviewRequired(packageName, permission, context)) {
+ if (isPermissionReviewRequiredAsUser(packageName, permission, user, context)) {
return false;
}
@@ -288,7 +299,7 @@ public class Permissions {
if (appOp == null) {
return true;
}
- Integer appOpMode = getAppOpMode(packageName, appOp, context);
+ Integer appOpMode = getAppOpModeAsUser(packageName, appOp, user, context);
if (appOpMode == null) {
return false;
}
@@ -314,7 +325,8 @@ public class Permissions {
if (foregroundAppOp == null) {
continue;
}
- Integer foregroundAppOpMode = getAppOpMode(packageName, foregroundAppOp, context);
+ Integer foregroundAppOpMode = getAppOpModeAsUser(packageName, foregroundAppOp,
+ user, context);
if (foregroundAppOpMode == null) {
continue;
}
@@ -326,11 +338,11 @@ public class Permissions {
}
}
- private static boolean grantPermissionAndAppOp(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
+ private static boolean grantPermissionAndAppOpAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
// Grant the permission.
- boolean permissionOrAppOpChanged = grantPermissionWithoutAppOp(packageName, permission,
- context);
+ boolean permissionOrAppOpChanged = grantPermissionWithoutAppOpAsUser(packageName,
+ permission, user, context);
// Grant the app op.
if (!isBackgroundPermission(permission, context)) {
@@ -345,13 +357,15 @@ public class Permissions {
// This permission is a foreground permission, set its app op mode according to
// whether its background permission is granted.
String backgroundPermission = getBackgroundPermission(permission, context);
- if (!isPermissionAndAppOpGranted(packageName, backgroundPermission, context)) {
+ if (!isPermissionAndAppOpGrantedAsUser(packageName, backgroundPermission,
+ user, context)) {
appOpMode = AppOpsManager.MODE_FOREGROUND;
} else {
appOpMode = AppOpsManager.MODE_ALLOWED;
}
}
- permissionOrAppOpChanged |= setAppOpUidMode(packageName, appOp, appOpMode, context);
+ permissionOrAppOpChanged |= setAppOpUidModeAsUser(packageName, appOp, appOpMode,
+ user, context);
}
} else {
// This permission is a background permission, set all its foreground permissions' app
@@ -365,8 +379,8 @@ public class Permissions {
if (foregroundAppOp == null) {
continue;
}
- permissionOrAppOpChanged |= setAppOpUidMode(packageName, foregroundAppOp,
- AppOpsManager.MODE_ALLOWED, context);
+ permissionOrAppOpChanged |= setAppOpUidModeAsUser(packageName, foregroundAppOp,
+ AppOpsManager.MODE_ALLOWED, user, context);
}
}
@@ -382,16 +396,18 @@ public class Permissions {
* @param onlyIfGrantedByDefault revoke the permission only if it is granted by default
* @param overrideSystemFixed whether system-fixed permissions can be revoked
* @param context the {@code Context} to retrieve system services
+ * @param user the user of the application
*
* @return whether any permission or app op changed
*
* @see com.android.server.pm.permission.DefaultPermissionGrantPolicy#revokeRuntimePermissions(
* String, java.util.Set, boolean, int)
*/
- public static boolean revoke(@NonNull String packageName, @NonNull List<String> permissions,
- boolean onlyIfGrantedByRole, boolean onlyIfGrantedByDefault,
- boolean overrideSystemFixed, @NonNull Context context) {
- PackageInfo packageInfo = getPackageInfo(packageName, context);
+ public static boolean revokeAsUser(@NonNull String packageName,
+ @NonNull List<String> permissions, boolean onlyIfGrantedByRole,
+ boolean onlyIfGrantedByDefault, boolean overrideSystemFixed, @NonNull UserHandle user,
+ @NonNull Context context) {
+ PackageInfo packageInfo = getPackageInfoAsUser(packageName, user, context);
if (packageInfo == null) {
return false;
}
@@ -425,9 +441,10 @@ public class Permissions {
}
}
- PackageManager packageManager = context.getPackageManager();
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
Set<String> whitelistedRestrictedPermissions =
- packageManager.getWhitelistedRestrictedPermissions(packageName,
+ userPackageManager.getWhitelistedRestrictedPermissions(packageName,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
| PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
| PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
@@ -438,13 +455,14 @@ public class Permissions {
for (int i = 0; i < sortedPermissionsToRevokeLength; i++) {
String permission = sortedPermissionsToRevoke[i];
- permissionOrAppOpChanged |= revokeSingle(packageName, permission, onlyIfGrantedByRole,
- onlyIfGrantedByDefault, overrideSystemFixed, context);
+ permissionOrAppOpChanged |= revokeSingleAsUser(packageName, permission,
+ onlyIfGrantedByRole, onlyIfGrantedByDefault, overrideSystemFixed, user,
+ context);
// Remove from the system whitelist only if not granted by default.
- if (!isPermissionGrantedByDefault(packageName, permission, context)
+ if (!isPermissionGrantedByDefaultAsUser(packageName, permission, user, context)
&& whitelistedRestrictedPermissions.remove(permission)) {
- packageManager.removeWhitelistedRestrictedPermission(packageName, permission,
+ userPackageManager.removeWhitelistedRestrictedPermission(packageName, permission,
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
}
}
@@ -452,59 +470,62 @@ public class Permissions {
return permissionOrAppOpChanged;
}
- private static boolean revokeSingle(@NonNull String packageName, @NonNull String permission,
- boolean onlyIfGrantedByRole, boolean onlyIfGrantedByDefault,
- boolean overrideSystemFixed, @NonNull Context context) {
+ private static boolean revokeSingleAsUser(@NonNull String packageName,
+ @NonNull String permission, boolean onlyIfGrantedByRole, boolean onlyIfGrantedByDefault,
+ boolean overrideSystemFixed, @NonNull UserHandle user, @NonNull Context context) {
if (onlyIfGrantedByRole == onlyIfGrantedByDefault) {
throw new IllegalArgumentException("Permission can be revoked only if either granted by"
+ " role, or granted by default, but not both");
}
if (onlyIfGrantedByRole) {
- if (!isPermissionGrantedByRole(packageName, permission, context)) {
+ if (!isPermissionGrantedByRoleAsUser(packageName, permission, user, context)) {
return false;
}
- setPermissionFlags(packageName, permission, 0,
- PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, context);
+ setPermissionFlagsAsUser(packageName, permission, 0,
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, user, context);
}
if (onlyIfGrantedByDefault) {
- if (!isPermissionGrantedByDefault(packageName, permission, context)) {
+ if (!isPermissionGrantedByDefaultAsUser(packageName, permission, user, context)) {
return false;
}
// Remove the granted-by-default permission flag.
- setPermissionFlags(packageName, permission, 0,
- PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, context);
+ setPermissionFlagsAsUser(packageName, permission, 0,
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, user, context);
// Note that we do not revoke FLAG_PERMISSION_SYSTEM_FIXED. That bit remains sticky once
// set.
}
- if (isPermissionFixed(packageName, permission, overrideSystemFixed, false, context)
- && isPermissionAndAppOpGranted(packageName, permission, context)) {
+ if (isPermissionFixedAsUser(packageName, permission, overrideSystemFixed, false,
+ user, context)
+ && isPermissionAndAppOpGrantedAsUser(packageName, permission, user, context)) {
// Stop revoking if this permission is fixed to granted.
return false;
}
if (isForegroundPermission(permission, context)) {
String backgroundPermission = getBackgroundPermission(permission, context);
- if (isPermissionAndAppOpGranted(packageName, backgroundPermission, context)) {
+ if (isPermissionAndAppOpGrantedAsUser(packageName, backgroundPermission, user,
+ context)) {
// Stop revoking if this foreground permission has a granted background permission.
return false;
}
}
- return revokePermissionAndAppOp(packageName, permission, context);
+ return revokePermissionAndAppOpAsUser(packageName, permission, user, context);
}
- private static boolean revokePermissionAndAppOp(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
+ private static boolean revokePermissionAndAppOpAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
boolean permissionOrAppOpChanged = false;
- boolean isRuntimePermissionsSupported = isRuntimePermissionsSupported(packageName, context);
+ boolean isRuntimePermissionsSupported = isRuntimePermissionsSupportedAsUser(packageName,
+ user, context);
if (isRuntimePermissionsSupported) {
// Revoke the permission.
- permissionOrAppOpChanged |= revokePermissionWithoutAppOp(packageName, permission,
- context);
+ permissionOrAppOpChanged |= revokePermissionWithoutAppOpAsUser(packageName, permission,
+ user, context);
}
// Revoke the app op.
@@ -514,7 +535,8 @@ public class Permissions {
// This permission is an ordinary or foreground permission, reset its app op mode to
// default.
int appOpMode = getDefaultAppOpMode(appOp);
- boolean appOpModeChanged = setAppOpUidMode(packageName, appOp, appOpMode, context);
+ boolean appOpModeChanged = setAppOpUidModeAsUser(packageName, appOp, appOpMode,
+ user, context);
permissionOrAppOpChanged |= appOpModeChanged;
if (appOpModeChanged) {
@@ -523,9 +545,9 @@ public class Permissions {
|| appOpMode == AppOpsManager.MODE_ALLOWED)) {
// We've reset this permission's app op mode to be permissive, so we'll need
// the user to review it again.
- setPermissionFlags(packageName, permission,
+ setPermissionFlagsAsUser(packageName, permission,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
- PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, context);
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, user, context);
}
}
}
@@ -537,7 +559,8 @@ public class Permissions {
for (int i = 0; i < foregroundPermissionsSize; i++) {
String foregroundPermission = foregroundPermissions.get(i);
- if (!isPermissionAndAppOpGranted(packageName, foregroundPermission, context)) {
+ if (!isPermissionAndAppOpGrantedAsUser(packageName, foregroundPermission, user,
+ context)) {
continue;
}
@@ -545,8 +568,8 @@ public class Permissions {
if (foregroundAppOp == null) {
continue;
}
- permissionOrAppOpChanged |= setAppOpUidMode(packageName, foregroundAppOp,
- AppOpsManager.MODE_FOREGROUND, context);
+ permissionOrAppOpChanged |= setAppOpUidModeAsUser(packageName, foregroundAppOp,
+ AppOpsManager.MODE_FOREGROUND, user, context);
}
}
@@ -554,24 +577,25 @@ public class Permissions {
}
@Nullable
- private static PackageInfo getPackageInfo(@NonNull String packageName,
- @NonNull Context context) {
- return getPackageInfo(packageName, 0, context);
+ private static PackageInfo getPackageInfoAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return getPackageInfoAsUser(packageName, 0, user, context);
}
@Nullable
- private static PackageInfo getFactoryPackageInfo(@NonNull String packageName,
- @NonNull Context context) {
- return getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY, context);
+ private static PackageInfo getFactoryPackageInfoAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return getPackageInfoAsUser(packageName, PackageManager.MATCH_FACTORY_ONLY,
+ user, context);
}
@Nullable
- private static PackageInfo getPackageInfo(@NonNull String packageName, int extraFlags,
- @NonNull Context context) {
- return PackageUtils.getPackageInfo(packageName, extraFlags
+ private static PackageInfo getPackageInfoAsUser(@NonNull String packageName, int extraFlags,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return PackageUtils.getPackageInfoAsUser(packageName, extraFlags
// TODO: Why MATCH_UNINSTALLED_PACKAGES?
| PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS,
- context);
+ user, context);
}
private static boolean isUpdatedSystemApp(@NonNull PackageInfo packageInfo) {
@@ -579,27 +603,26 @@ public class Permissions {
& ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
- static boolean isRuntimePermissionsSupported(@NonNull String packageName,
- @NonNull Context context) {
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ static boolean isRuntimePermissionsSupportedAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
+ context);
if (applicationInfo == null) {
return false;
}
return applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
}
- private static int getPermissionFlags(@NonNull String packageName, @NonNull String permission,
- @NonNull Context context) {
+ private static int getPermissionFlagsAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
PackageManager packageManager = context.getPackageManager();
- UserHandle user = Process.myUserHandle();
return packageManager.getPermissionFlags(permission, packageName, user);
}
- private static boolean isPermissionFixed(@NonNull String packageName,
+ private static boolean isPermissionFixedAsUser(@NonNull String packageName,
@NonNull String permission, boolean overrideSystemFixed,
- boolean overrideUserSetAndFixed, @NonNull Context context) {
- int flags = getPermissionFlags(packageName, permission, context);
+ boolean overrideUserSetAndFixed, @NonNull UserHandle user, @NonNull Context context) {
+ int flags = getPermissionFlagsAsUser(packageName, permission, user, context);
int fixedFlags = PackageManager.FLAG_PERMISSION_POLICY_FIXED;
if (!overrideSystemFixed) {
fixedFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
@@ -611,67 +634,69 @@ public class Permissions {
return (flags & fixedFlags) != 0;
}
- private static boolean isPermissionGrantedByDefault(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
- int flags = getPermissionFlags(packageName, permission, context);
+ private static boolean isPermissionGrantedByDefaultAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
+ int flags = getPermissionFlagsAsUser(packageName, permission, user, context);
return (flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
}
- static boolean isPermissionGrantedByRole(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
- int flags = getPermissionFlags(packageName, permission, context);
+ static boolean isPermissionGrantedByRoleAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
+ int flags = getPermissionFlagsAsUser(packageName, permission, user, context);
return (flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE) != 0;
}
- private static boolean isPermissionReviewRequired(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
- int flags = getPermissionFlags(packageName, permission, context);
+ private static boolean isPermissionReviewRequiredAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
+ int flags = getPermissionFlagsAsUser(packageName, permission, user, context);
return (flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
}
- private static void setPermissionFlags(@NonNull String packageName, @NonNull String permission,
- int flags, int mask, @NonNull Context context) {
+ private static void setPermissionFlagsAsUser(@NonNull String packageName,
+ @NonNull String permission, int flags, int mask, @NonNull UserHandle user,
+ @NonNull Context context) {
PackageManager packageManager = context.getPackageManager();
- UserHandle user = Process.myUserHandle();
packageManager.updatePermissionFlags(permission, packageName, mask, flags, user);
}
- static void setPermissionGrantedByRole(@NonNull String packageName,
- @NonNull String permission, boolean grantedByRole, @NonNull Context context) {
- setPermissionFlags(packageName, permission,
+ static void setPermissionGrantedByRoleAsUser(@NonNull String packageName,
+ @NonNull String permission, boolean grantedByRole, @NonNull UserHandle user,
+ @NonNull Context context) {
+ setPermissionFlagsAsUser(packageName, permission,
grantedByRole ? PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE : 0,
- PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, context);
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, user, context);
}
/**
* Most of the time {@link #isPermissionAndAppOpGranted(String, String, Context)} should be used
* instead.
*/
- private static boolean isPermissionGrantedWithoutCheckingAppOp(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
- PackageManager packageManager = context.getPackageManager();
- return packageManager.checkPermission(permission, packageName)
+ private static boolean isPermissionGrantedWithoutCheckingAppOpAsUser(
+ @NonNull String packageName, @NonNull String permission, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ return userPackageManager.checkPermission(permission, packageName)
== PackageManager.PERMISSION_GRANTED;
}
- private static boolean grantPermissionWithoutAppOp(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
- if (isPermissionGrantedWithoutCheckingAppOp(packageName, permission, context)) {
+ private static boolean grantPermissionWithoutAppOpAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
+ if (isPermissionGrantedWithoutCheckingAppOpAsUser(packageName, permission, user, context)) {
return false;
}
PackageManager packageManager = context.getPackageManager();
- UserHandle user = Process.myUserHandle();
packageManager.grantRuntimePermission(packageName, permission, user);
return true;
}
- private static boolean revokePermissionWithoutAppOp(@NonNull String packageName,
- @NonNull String permission, @NonNull Context context) {
- if (!isPermissionGrantedWithoutCheckingAppOp(packageName, permission, context)) {
+ private static boolean revokePermissionWithoutAppOpAsUser(@NonNull String packageName,
+ @NonNull String permission, @NonNull UserHandle user, @NonNull Context context) {
+ if (!isPermissionGrantedWithoutCheckingAppOpAsUser(packageName, permission, user,
+ context)) {
return false;
}
PackageManager packageManager = context.getPackageManager();
- UserHandle user = Process.myUserHandle();
packageManager.revokeRuntimePermission(packageName, permission, user);
return true;
}
@@ -816,10 +841,10 @@ public class Permissions {
}
@Nullable
- static Integer getAppOpMode(@NonNull String packageName, @NonNull String appOp,
- @NonNull Context context) {
+ static Integer getAppOpModeAsUser(@NonNull String packageName, @NonNull String appOp,
+ @NonNull UserHandle user, @NonNull Context context) {
ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ user, context);
if (applicationInfo == null) {
return null;
}
@@ -831,24 +856,24 @@ public class Permissions {
return AppOpsManager.opToDefaultMode(appOp);
}
- static boolean setAppOpUidMode(@NonNull String packageName, @NonNull String appOp, int mode,
- @NonNull Context context) {
- return setAppOpMode(packageName, appOp, mode, true, context);
+ static boolean setAppOpUidModeAsUser(@NonNull String packageName, @NonNull String appOp,
+ int mode, @NonNull UserHandle user, @NonNull Context context) {
+ return setAppOpModeAsUser(packageName, appOp, mode, true, user, context);
}
- static boolean setAppOpPackageMode(@NonNull String packageName, @NonNull String appOp, int mode,
- @NonNull Context context) {
- return setAppOpMode(packageName, appOp, mode, false, context);
+ static boolean setAppOpPackageModeAsUser(@NonNull String packageName, @NonNull String appOp,
+ int mode, @NonNull UserHandle user, @NonNull Context context) {
+ return setAppOpModeAsUser(packageName, appOp, mode, false, user, context);
}
- private static boolean setAppOpMode(@NonNull String packageName, @NonNull String appOp,
- int mode, boolean setUidMode, @NonNull Context context) {
- Integer currentMode = getAppOpMode(packageName, appOp, context);
+ private static boolean setAppOpModeAsUser(@NonNull String packageName, @NonNull String appOp,
+ int mode, boolean setUidMode, @NonNull UserHandle user, @NonNull Context context) {
+ Integer currentMode = getAppOpModeAsUser(packageName, appOp, user, context);
if (currentMode != null && currentMode == mode) {
return false;
}
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
+ context);
if (applicationInfo == null) {
Log.e(LOG_TAG, "Cannot get ApplicationInfo for package to set app op mode: "
+ packageName);
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/PreferredActivity.java b/PermissionController/role-controller/java/com/android/role/controller/model/PreferredActivity.java
index 5b9c22b67..7ea7de046 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/PreferredActivity.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/PreferredActivity.java
@@ -22,9 +22,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
import androidx.annotation.NonNull;
+import com.android.role.controller.util.UserUtils;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -67,18 +70,21 @@ public class PreferredActivity {
* Configure this preferred activity specification for an application.
*
* @param packageName the package name of the application
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*/
- public void configure(@NonNull String packageName, @NonNull Context context) {
- ComponentName packageActivity = mActivity.getQualifyingComponentForPackage(
- packageName, context);
+ public void configureAsUser(@NonNull String packageName, @NonNull UserHandle user,
+ @NonNull Context context) {
+ ComponentName packageActivity = mActivity.getQualifyingComponentForPackageAsUser(
+ packageName, user, context);
if (packageActivity == null) {
// We might be running into some race condition here, but we can't do anything about it.
// This should be handled by a future reconciliation started by the package change.
return;
}
- PackageManager packageManager = context.getPackageManager();
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
int intentFilterDatasSize = mIntentFilterDatas.size();
for (int i = 0; i < intentFilterDatasSize; i++) {
IntentFilterData intentFilterData = mIntentFilterDatas.get(i);
@@ -92,7 +98,7 @@ public class PreferredActivity {
? IntentFilter.MATCH_CATEGORY_SCHEME : IntentFilter.MATCH_CATEGORY_EMPTY;
Intent intent = intentFilterData.createIntent();
- List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent,
+ List<ResolveInfo> resolveInfos = userPackageManager.queryIntentActivities(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DEFAULT_ONLY);
@@ -107,7 +113,7 @@ public class PreferredActivity {
set.add(componentName);
}
- packageManager.replacePreferredActivity(intentFilter, match, set, packageActivity);
+ userPackageManager.replacePreferredActivity(intentFilter, match, set, packageActivity);
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredActivity.java b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredActivity.java
index 58c878e56..25c97aefb 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredActivity.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredActivity.java
@@ -16,12 +16,11 @@
package com.android.role.controller.model;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Bundle;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -52,11 +51,15 @@ public class RequiredActivity extends RequiredComponent {
return userPackageManager.queryIntentActivities(intent, flags);
}
+ @Override
+ protected boolean isComponentQualified(@NonNull ResolveInfo resolveInfo) {
+ return resolveInfo.activityInfo.exported;
+ }
+
@NonNull
@Override
- protected ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo) {
- return new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name);
+ protected ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo) {
+ return resolveInfo.activityInfo;
}
@Override
@@ -69,10 +72,4 @@ public class RequiredActivity extends RequiredComponent {
protected String getComponentPermission(@NonNull ResolveInfo resolveInfo) {
return resolveInfo.activityInfo.permission;
}
-
- @Nullable
- @Override
- protected Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo) {
- return resolveInfo.activityInfo.metaData;
- }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredBroadcastReceiver.java b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredBroadcastReceiver.java
index 945fda3c3..1e23af256 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredBroadcastReceiver.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredBroadcastReceiver.java
@@ -16,12 +16,11 @@
package com.android.role.controller.model;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Bundle;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -53,9 +52,8 @@ public class RequiredBroadcastReceiver extends RequiredComponent {
@NonNull
@Override
- protected ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo) {
- return new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name);
+ protected ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo) {
+ return resolveInfo.activityInfo;
}
@Override
@@ -68,10 +66,4 @@ public class RequiredBroadcastReceiver extends RequiredComponent {
protected String getComponentPermission(@NonNull ResolveInfo resolveInfo) {
return resolveInfo.activityInfo.permission;
}
-
- @Nullable
- @Override
- protected Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo) {
- return resolveInfo.activityInfo.metaData;
- }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredComponent.java b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredComponent.java
index ae6156e7f..6e067488c 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredComponent.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredComponent.java
@@ -20,11 +20,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
-import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -108,9 +108,9 @@ public abstract class RequiredComponent {
* @return whether this required component is available
*/
public boolean isAvailable() {
- // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization.
- if (mMinTargetSdkVersion >= 34) {
- return SdkLevel.isAtLeastU();
+ // Workaround to match the value 35+ for V+ in roles.xml before SDK finalization.
+ if (mMinTargetSdkVersion >= 35) {
+ return SdkLevel.isAtLeastV();
} else {
return Build.VERSION.SDK_INT >= mMinTargetSdkVersion;
}
@@ -144,15 +144,16 @@ public abstract class RequiredComponent {
* Get the component that matches this required component within a package, if any.
*
* @param packageName the package name for this query
+ * @param user the user of the component
* @param context the {@code Context} to retrieve system services
*
* @return the matching component, or {@code null} if none.
*/
@Nullable
- public ComponentName getQualifyingComponentForPackage(@NonNull String packageName,
- @NonNull Context context) {
- List<ComponentName> componentNames = getQualifyingComponentsInternal(packageName,
- Process.myUserHandle(), context);
+ public ComponentName getQualifyingComponentForPackageAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ List<ComponentName> componentNames = getQualifyingComponentsAsUserInternal(packageName,
+ user, context);
return !componentNames.isEmpty() ? componentNames.get(0) : null;
}
@@ -170,11 +171,11 @@ public abstract class RequiredComponent {
@NonNull
public List<ComponentName> getQualifyingComponentsAsUser(@NonNull UserHandle user,
@NonNull Context context) {
- return getQualifyingComponentsInternal(null, user, context);
+ return getQualifyingComponentsAsUserInternal(null, user, context);
}
@NonNull
- private List<ComponentName> getQualifyingComponentsInternal(@Nullable String packageName,
+ private List<ComponentName> getQualifyingComponentsAsUserInternal(@Nullable String packageName,
@NonNull UserHandle user, @NonNull Context context) {
Intent intent = mIntentFilterData.createIntent();
if (packageName != null) {
@@ -195,6 +196,10 @@ public abstract class RequiredComponent {
for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; resolveInfosIndex++) {
ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex);
+ if (!isComponentQualified(resolveInfo)) {
+ continue;
+ }
+
if (mFlags != 0) {
int componentFlags = getComponentFlags(resolveInfo);
if ((componentFlags & mFlags) != mFlags) {
@@ -209,8 +214,9 @@ public abstract class RequiredComponent {
}
}
+ ComponentInfo componentInfo = getComponentComponentInfo(resolveInfo);
if (hasMetaData) {
- Bundle componentMetaData = getComponentMetaData(resolveInfo);
+ Bundle componentMetaData = componentInfo.metaData;
if (componentMetaData == null) {
componentMetaData = Bundle.EMPTY;
}
@@ -229,13 +235,14 @@ public abstract class RequiredComponent {
}
}
- ComponentName componentName = getComponentComponentName(resolveInfo);
- String componentPackageName = componentName.getPackageName();
+ String componentPackageName = componentInfo.packageName;
if (componentPackageNames.contains(componentPackageName)) {
continue;
}
-
componentPackageNames.add(componentPackageName);
+
+ ComponentName componentName = new ComponentName(componentPackageName,
+ componentInfo.name);
componentNames.add(componentName);
}
return componentNames;
@@ -256,15 +263,19 @@ public abstract class RequiredComponent {
protected abstract List<ResolveInfo> queryIntentComponentsAsUser(@NonNull Intent intent,
int flags, @NonNull UserHandle user, @NonNull Context context);
+ protected boolean isComponentQualified(@NonNull ResolveInfo resolveInfo) {
+ return true;
+ }
+
/**
- * Get the {@code ComponentName} of a component.
+ * Get the {@code ComponentInfo} of a component.
*
* @param resolveInfo the {@code ResolveInfo} of the component
*
- * @return the {@code ComponentName} of the component
+ * @return the {@code ComponentInfo} of the component
*/
@NonNull
- protected abstract ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo);
+ protected abstract ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo);
/**
* Get the flags that have been set on a component.
@@ -285,16 +296,6 @@ public abstract class RequiredComponent {
@Nullable
protected abstract String getComponentPermission(@NonNull ResolveInfo resolveInfo);
- /**
- * Get the meta data associated with a component.
- *
- * @param resolveInfo the {@code ResolveInfo} of the component
- *
- * @return the meta data associated with a component
- */
- @Nullable
- protected abstract Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo);
-
@Override
public String toString() {
return "RequiredComponent{"
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredContentProvider.java b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredContentProvider.java
index 7b53a25bb..b02062b11 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredContentProvider.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredContentProvider.java
@@ -16,12 +16,11 @@
package com.android.role.controller.model;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Bundle;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -53,9 +52,8 @@ public class RequiredContentProvider extends RequiredComponent {
@NonNull
@Override
- protected ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo) {
- return new ComponentName(resolveInfo.providerInfo.packageName,
- resolveInfo.providerInfo.name);
+ protected ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo) {
+ return resolveInfo.providerInfo;
}
@Override
@@ -70,10 +68,4 @@ public class RequiredContentProvider extends RequiredComponent {
//return resolveInfo.providerInfo.readPermission;
throw new UnsupportedOperationException();
}
-
- @Nullable
- @Override
- protected Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo) {
- return resolveInfo.providerInfo.metaData;
- }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredService.java b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredService.java
index f27aae013..0532e53b2 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RequiredService.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RequiredService.java
@@ -16,12 +16,11 @@
package com.android.role.controller.model;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Bundle;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -53,8 +52,8 @@ public class RequiredService extends RequiredComponent {
@NonNull
@Override
- protected ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo) {
- return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
+ protected ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo) {
+ return resolveInfo.serviceInfo;
}
@Override
@@ -67,10 +66,4 @@ public class RequiredService extends RequiredComponent {
protected String getComponentPermission(@NonNull ResolveInfo resolveInfo) {
return resolveInfo.serviceInfo.permission;
}
-
- @Nullable
- @Override
- protected Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo) {
- return resolveInfo.serviceInfo.metaData;
- }
}
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..c3541c83b 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
@@ -17,17 +17,22 @@
package com.android.role.controller.model;
import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.os.Build;
-import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.permission.flags.Flags;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -129,6 +134,11 @@ public class Role {
private final int mMinSdkVersion;
/**
+ * Whether this role should only grant privileges when a role holder is actively added.
+ */
+ private final boolean mOnlyGrantWhenAdded;
+
+ /**
* Whether this role should override user's choice about privileges when granting.
*/
private final boolean mOverrideUserWhenGranting;
@@ -225,11 +235,11 @@ public class Role {
@Nullable RoleBehavior behavior, @Nullable String defaultHoldersResourceName,
@StringRes int descriptionResource, boolean exclusive, boolean fallBackToDefaultHolder,
@StringRes int labelResource, int maxSdkVersion, int minSdkVersion,
- boolean overrideUserWhenGranting, @StringRes int requestDescriptionResource,
- @StringRes int requestTitleResource, boolean requestable,
- @StringRes int searchKeywordsResource, @StringRes int shortLabelResource,
- boolean showNone, boolean statik, boolean systemOnly, boolean visible,
- @NonNull List<RequiredComponent> requiredComponents,
+ boolean onlyGrantWhenAdded, boolean overrideUserWhenGranting,
+ @StringRes int requestDescriptionResource, @StringRes int requestTitleResource,
+ boolean requestable, @StringRes int searchKeywordsResource,
+ @StringRes int shortLabelResource, boolean showNone, boolean statik, boolean systemOnly,
+ boolean visible, @NonNull List<RequiredComponent> requiredComponents,
@NonNull List<Permission> permissions, @NonNull List<Permission> appOpPermissions,
@NonNull List<AppOp> appOps, @NonNull List<PreferredActivity> preferredActivities,
@Nullable String uiBehaviorName) {
@@ -243,6 +253,7 @@ public class Role {
mLabelResource = labelResource;
mMaxSdkVersion = maxSdkVersion;
mMinSdkVersion = minSdkVersion;
+ mOnlyGrantWhenAdded = onlyGrantWhenAdded;
mOverrideUserWhenGranting = overrideUserWhenGranting;
mRequestDescriptionResource = requestDescriptionResource;
mRequestTitleResource = requestTitleResource;
@@ -310,6 +321,13 @@ public class Role {
}
/**
+ * @see #mOnlyGrantWhenAdded
+ */
+ public boolean shouldOnlyGrantWhenAdded() {
+ return mOnlyGrantWhenAdded;
+ }
+
+ /**
* @see #mOverrideUserWhenGranting
*/
public boolean shouldOverrideUserWhenGranting() {
@@ -360,11 +378,12 @@ public class Role {
/**
* Callback when this role is added to the system for the first time.
*
+ * @param user the user to add the role for
* @param context the {@code Context} to retrieve system services
*/
- public void onRoleAdded(@NonNull Context context) {
+ public void onRoleAddedAsUser(@NonNull UserHandle user, @NonNull Context context) {
if (mBehavior != null) {
- mBehavior.onRoleAdded(this, context);
+ mBehavior.onRoleAddedAsUser(this, user, context);
}
}
@@ -392,24 +411,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;
- }
- }
-
- /**
- * Check whether this role is available, for current user.
- *
- * @param context the {@code Context} to retrieve system services
- *
- * @return whether this role is available.
- */
- public boolean isAvailable(@NonNull Context context) {
- return isAvailableAsUser(Process.myUserHandle(), context);
+ return (Build.VERSION.SDK_INT >= mMinSdkVersion
+ // Workaround to match the value 35 for V in roles.xml before SDK finalization.
+ || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV()))
+ && Build.VERSION.SDK_INT <= mMaxSdkVersion;
}
public boolean isStatic() {
@@ -420,16 +425,21 @@ public class Role {
* Get the default holders of this role, which will be added when the role is added for the
* first time.
*
+ * @param user the user of the role
* @param context the {@code Context} to retrieve system services
- *
* @return the list of package names of the default holders
*/
@NonNull
- public List<String> getDefaultHolders(@NonNull Context context) {
- if (mDefaultHoldersResourceName == null) {
- if (mBehavior != null) {
- return mBehavior.getDefaultHolders(this, context);
+ public List<String> getDefaultHoldersAsUser(@NonNull UserHandle user,
+ @NonNull Context context) {
+ if (mBehavior != null) {
+ List<String> defaultHolders = mBehavior.getDefaultHoldersAsUser(this, user, context);
+ if (defaultHolders != null) {
+ return defaultHolders;
}
+ }
+
+ if (mDefaultHoldersResourceName == null) {
return Collections.emptyList();
}
@@ -454,7 +464,8 @@ public class Role {
}
if (isExclusive()) {
- String packageName = getQualifiedDefaultHolderPackageName(defaultHolders, context);
+ String packageName = getQualifiedDefaultHolderPackageNameAsUser(defaultHolders, user,
+ context);
if (packageName == null) {
return Collections.emptyList();
}
@@ -462,7 +473,8 @@ public class Role {
} else {
List<String> packageNames = new ArrayList<>();
for (String defaultHolder : defaultHolders.split(DEFAULT_HOLDER_SEPARATOR)) {
- String packageName = getQualifiedDefaultHolderPackageName(defaultHolders, context);
+ String packageName = getQualifiedDefaultHolderPackageNameAsUser(defaultHolder,
+ user, context);
if (packageName != null) {
packageNames.add(packageName);
}
@@ -472,8 +484,8 @@ public class Role {
}
@Nullable
- private String getQualifiedDefaultHolderPackageName(@NonNull String defaultHolder,
- @NonNull Context context) {
+ private String getQualifiedDefaultHolderPackageNameAsUser(@NonNull String defaultHolder,
+ @NonNull UserHandle user, @NonNull Context context) {
String packageName;
byte[] certificate;
int certificateSeparatorIndex = defaultHolder.indexOf(CERTIFICATE_SEPARATOR);
@@ -492,8 +504,9 @@ public class Role {
}
if (certificate != null) {
- PackageManager packageManager = context.getPackageManager();
- if (!packageManager.hasSigningCertificate(packageName, certificate,
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ if (!userPackageManager.hasSigningCertificate(packageName, certificate,
PackageManager.CERT_INPUT_SHA256)) {
Log.w(LOG_TAG, "Default holder doesn't have required signing certificate: "
+ defaultHolder);
@@ -501,7 +514,7 @@ public class Role {
}
} else {
ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ user, context);
if (applicationInfo == null) {
Log.w(LOG_TAG, "Cannot get ApplicationInfo for default holder: " + packageName);
return null;
@@ -521,20 +534,20 @@ public class Role {
* <p>
* Should return {@code null} if this role {@link #mShowNone shows a "None" item}.
*
+ * @param user the user of the role
* @param context the {@code Context} to retrieve system services
- *
* @return the package name of the fallback holder, or {@code null} if none
*/
@Nullable
- public String getFallbackHolder(@NonNull Context context) {
- if (!RoleManagerCompat.isRoleFallbackEnabledAsUser(this, Process.myUserHandle(), context)) {
+ public String getFallbackHolderAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ if (!RoleManagerCompat.isRoleFallbackEnabledAsUser(this, user, context)) {
return null;
}
if (mFallBackToDefaultHolder) {
- return CollectionUtils.firstOrNull(getDefaultHolders(context));
+ return CollectionUtils.firstOrNull(getDefaultHoldersAsUser(user, context));
}
if (mBehavior != null) {
- return mBehavior.getFallbackHolder(this, context);
+ return mBehavior.getFallbackHolderAsUser(this, user, context);
}
return null;
}
@@ -562,29 +575,32 @@ public class Role {
* components (plus meeting some other general restrictions).
*
* @param packageName the package name to check for
+ * @param user the user to check for
* @param context the {@code Context} to retrieve system services
*
* @return whether the package is qualified for a role
*/
- public boolean isPackageQualified(@NonNull String packageName, @NonNull Context context) {
+ public boolean isPackageQualifiedAsUser(@NonNull String packageName, @NonNull UserHandle user,
+ @NonNull Context context) {
RoleManager roleManager = context.getSystemService(RoleManager.class);
if (shouldAllowBypassingQualification(context)
&& RoleManagerCompat.isBypassingRoleQualification(roleManager)) {
return true;
}
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
+ context);
if (applicationInfo == null) {
Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
return false;
}
- if (!isPackageMinimallyQualifiedAsUser(applicationInfo, Process.myUserHandle(), context)) {
+ if (!isPackageMinimallyQualifiedAsUser(applicationInfo, user, context)) {
return false;
}
if (mBehavior != null) {
- Boolean isPackageQualified = mBehavior.isPackageQualified(this, packageName, context);
+ Boolean isPackageQualified = mBehavior.isPackageQualifiedAsUser(this, packageName,
+ user, context);
if (isPackageQualified != null) {
return isPackageQualified;
}
@@ -598,14 +614,15 @@ public class Role {
continue;
}
- if (requiredComponent.getQualifyingComponentForPackage(packageName, context) == null) {
+ if (requiredComponent.getQualifyingComponentForPackageAsUser(packageName, user, context)
+ == null) {
Log.i(LOG_TAG, packageName + " not qualified for " + mName
+ " due to missing " + requiredComponent);
return false;
}
}
- if (mStatic && !getDefaultHolders(context).contains(packageName)) {
+ if (mStatic && !getDefaultHoldersAsUser(user, context).contains(packageName)) {
return false;
}
@@ -778,41 +795,43 @@ public class Role {
* @param packageName the package name of the application to be granted this role to
* @param dontKillApp whether this application should not be killed despite changes
* @param overrideUser whether to override user when granting privileges
+ * @param user the user of the application
* @param context the {@code Context} to retrieve system services
*/
- public void grant(@NonNull String packageName, boolean dontKillApp,
- boolean overrideUser, @NonNull Context context) {
- boolean permissionOrAppOpChanged = Permissions.grant(packageName,
- Permissions.filterBySdkVersion(mPermissions),
+ public void grantAsUser(@NonNull String packageName, boolean dontKillApp,
+ boolean overrideUser, @NonNull UserHandle user, @NonNull Context context) {
+ boolean permissionOrAppOpChanged = Permissions.grantAsUser(packageName,
+ Permissions.filterBySdkVersionAsUser(mPermissions, user, context),
SdkLevel.isAtLeastS() ? !mSystemOnly : true, overrideUser, true, false, false,
- context);
+ user, context);
- List<String> appOpPermissionsToGrant = Permissions.filterBySdkVersion(mAppOpPermissions);
+ List<String> appOpPermissionsToGrant =
+ Permissions.filterBySdkVersionAsUser(mAppOpPermissions, user, context);
int appOpPermissionsSize = appOpPermissionsToGrant.size();
for (int i = 0; i < appOpPermissionsSize; i++) {
String appOpPermission = appOpPermissionsToGrant.get(i);
- AppOpPermissions.grant(packageName, appOpPermission, overrideUser, context);
+ AppOpPermissions.grantAsUser(packageName, appOpPermission, overrideUser, user, context);
}
int appOpsSize = mAppOps.size();
for (int i = 0; i < appOpsSize; i++) {
AppOp appOp = mAppOps.get(i);
- appOp.grant(packageName, context);
+ appOp.grantAsUser(packageName, user, context);
}
int preferredActivitiesSize = mPreferredActivities.size();
for (int i = 0; i < preferredActivitiesSize; i++) {
PreferredActivity preferredActivity = mPreferredActivities.get(i);
- preferredActivity.configure(packageName, context);
+ preferredActivity.configureAsUser(packageName, user, context);
}
if (mBehavior != null) {
- mBehavior.grant(this, packageName, context);
+ mBehavior.grantAsUser(this, packageName, user, context);
}
- if (!dontKillApp && permissionOrAppOpChanged && !Permissions.isRuntimePermissionsSupported(
- packageName, context)) {
- killApp(packageName, context);
+ if (!dontKillApp && permissionOrAppOpChanged
+ && !Permissions.isRuntimePermissionsSupportedAsUser(packageName, user, context)) {
+ killAppAsUser(packageName, user, context);
}
}
@@ -822,37 +841,43 @@ public class Role {
* @param packageName the package name of the application to be granted this role to
* @param dontKillApp whether this application should not be killed despite changes
* @param overrideSystemFixedPermissions whether system-fixed permissions can be revoked
+ * @param user the user of the role
* @param context the {@code Context} to retrieve system services
*/
- public void revoke(@NonNull String packageName, boolean dontKillApp,
- boolean overrideSystemFixedPermissions, @NonNull Context context) {
- RoleManager roleManager = context.getSystemService(RoleManager.class);
- List<String> otherRoleNames = roleManager.getHeldRolesFromController(packageName);
+ public void revokeAsUser(@NonNull String packageName, boolean dontKillApp,
+ boolean overrideSystemFixedPermissions, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ List<String> otherRoleNames = userRoleManager.getHeldRolesFromController(packageName);
otherRoleNames.remove(mName);
- List<String> permissionsToRevoke = Permissions.filterBySdkVersion(mPermissions);
+ List<String> permissionsToRevoke =
+ Permissions.filterBySdkVersionAsUser(mPermissions, user, 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.filterBySdkVersionAsUser(role.mPermissions, user, context));
}
- boolean permissionOrAppOpChanged = Permissions.revoke(packageName, permissionsToRevoke,
- true, false, overrideSystemFixedPermissions, context);
+ boolean permissionOrAppOpChanged = Permissions.revokeAsUser(packageName,
+ permissionsToRevoke, true, false, overrideSystemFixedPermissions, user, context);
- List<String> appOpPermissionsToRevoke = Permissions.filterBySdkVersion(mAppOpPermissions);
+ List<String> appOpPermissionsToRevoke = Permissions.filterBySdkVersionAsUser(
+ mAppOpPermissions, user, 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.filterBySdkVersionAsUser(role.mAppOpPermissions, user, context));
}
int appOpPermissionsSize = appOpPermissionsToRevoke.size();
for (int i = 0; i < appOpPermissionsSize; i++) {
String appOpPermission = appOpPermissionsToRevoke.get(i);
- AppOpPermissions.revoke(packageName, appOpPermission, context);
+ AppOpPermissions.revokeAsUser(packageName, appOpPermission, user, context);
}
List<AppOp> appOpsToRevoke = new ArrayList<>(mAppOps);
@@ -864,7 +889,7 @@ public class Role {
int appOpsSize = appOpsToRevoke.size();
for (int i = 0; i < appOpsSize; i++) {
AppOp appOp = appOpsToRevoke.get(i);
- appOp.revoke(packageName, context);
+ appOp.revokeAsUser(packageName, user, context);
}
// TODO: Revoke preferred activities? But this is unnecessary for most roles using it as
@@ -873,22 +898,23 @@ public class Role {
// wrong thing when we are removing a exclusive role holder for adding another.
if (mBehavior != null) {
- mBehavior.revoke(this, packageName, context);
+ mBehavior.revokeAsUser(this, packageName, user, context);
}
if (!dontKillApp && permissionOrAppOpChanged) {
- killApp(packageName, context);
+ killAppAsUser(packageName, user, context);
}
}
- private void killApp(@NonNull String packageName, @NonNull Context context) {
+ private void killAppAsUser(@NonNull String packageName, @NonNull UserHandle user,
+ @NonNull Context context) {
if (DEBUG) {
Log.i(LOG_TAG, "Killing " + packageName + " due to "
+ Thread.currentThread().getStackTrace()[3].getMethodName()
+ "(" + mName + ")");
}
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- Process.myUserHandle(), context);
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName, user,
+ context);
if (applicationInfo == null) {
Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
return;
@@ -947,6 +973,110 @@ public class Role {
RoleManagerCompat.setRoleFallbackEnabledAsUser(this, false, user, context);
}
+ /**
+ * Check whether this role should be visible to user.
+ *
+ * @param user the user to check for
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return whether this role should be visible to user
+ */
+ public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ RoleBehavior behavior = getBehavior();
+ if (behavior == null) {
+ return isVisible();
+ }
+ return isVisible() && behavior.isVisibleAsUser(this, user, context);
+ }
+
+ /**
+ * Check whether a qualifying application should be visible to user.
+ *
+ * @param applicationInfo the {@link ApplicationInfo} for the application
+ * @param user the user for the application
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return whether the qualifying application should be visible to user
+ */
+ public boolean isApplicationVisibleAsUser(@NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
+ RoleBehavior behavior = getBehavior();
+ if (behavior == null) {
+ return true;
+ }
+ return behavior.isApplicationVisibleAsUser(this, applicationInfo, user, context);
+ }
+
+ /**
+ * Check whether this role is restricted and return the {@code Intent} for the restriction if it
+ * is.
+ * <p>
+ * If a role is restricted, it is implied that all applications are restricted for the role as
+ * well.
+ *
+ * @param user the user to check for
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the {@code Intent} for the restriction if this role is restricted, or {@code null}
+ * otherwise.
+ */
+ @Nullable
+ public Intent getRestrictionIntentAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ if (SdkLevel.isAtLeastU() && mExclusive) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ if (userManager.hasUserRestrictionForUser(UserManager.DISALLOW_CONFIG_DEFAULT_APPS,
+ user)) {
+ return new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)
+ .putExtra(DevicePolicyManager.EXTRA_RESTRICTION,
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check whether an application is restricted for this role and return the {@code Intent} for
+ * the restriction if it is.
+ * <p>
+ * If a role is restricted, it is implied that all applications are restricted for the role as
+ * well.
+ *
+ * @param applicationInfo the {@link ApplicationInfo} for the application
+ * @param user the user to check for
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the {@code Intent} for the restriction if the application is restricted for this
+ * role, or {@code null} otherwise.
+ */
+ @Nullable
+ public Intent getApplicationRestrictionIntentAsUser(@NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
+ if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ EnhancedConfirmationManager userEnhancedConfirmationManager =
+ userContext.getSystemService(EnhancedConfirmationManager.class);
+ String packageName = applicationInfo.packageName;
+ boolean isRestricted;
+ try {
+ isRestricted = userEnhancedConfirmationManager.isRestricted(packageName, mName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "Cannot check enhanced confirmation restriction for package: "
+ + packageName, e);
+ isRestricted = false;
+ }
+ if (isRestricted) {
+ try {
+ return userEnhancedConfirmationManager.createRestrictedSettingDialogIntent(
+ packageName, mName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "Cannot create enhanced confirmation restriction intent for"
+ + " package: " + packageName, e);
+ }
+ }
+ }
+ return getRestrictionIntentAsUser(user, context);
+ }
+
@Override
public String toString() {
return "Role{"
@@ -960,6 +1090,7 @@ public class Role {
+ ", mLabelResource=" + mLabelResource
+ ", mMaxSdkVersion=" + mMaxSdkVersion
+ ", mMinSdkVersion=" + mMinSdkVersion
+ + ", mOnlyGrantWhenAdded=" + mOnlyGrantWhenAdded
+ ", mOverrideUserWhenGranting=" + mOverrideUserWhenGranting
+ ", mRequestDescriptionResource=" + mRequestDescriptionResource
+ ", mRequestTitleResource=" + mRequestTitleResource
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java
index f0c4fc018..3849a50e3 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java
@@ -17,6 +17,7 @@
package com.android.role.controller.model;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
import androidx.annotation.NonNull;
@@ -31,9 +32,10 @@ import java.util.List;
public interface RoleBehavior {
/**
- * @see Role#onRoleAdded(Context)
+ * @see Role#onRoleAddedAsUser(UserHandle, Context)
*/
- default void onRoleAdded(@NonNull Role role, @NonNull Context context) {}
+ default void onRoleAddedAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {}
/**
* @see Role#isAvailableAsUser(UserHandle, Context)
@@ -46,16 +48,18 @@ public interface RoleBehavior {
/**
* @see Role#getDefaultHolders(Context)
*/
- @NonNull
- default List<String> getDefaultHolders(@NonNull Role role, @NonNull Context context) {
- return Collections.emptyList();
+ @Nullable
+ default List<String> getDefaultHoldersAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return null;
}
/**
* @see Role#getFallbackHolder(Context)
*/
@Nullable
- default String getFallbackHolder(@NonNull Role role, @NonNull Context context) {
+ default String getFallbackHolderAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
return null;
}
@@ -72,8 +76,8 @@ public interface RoleBehavior {
* @see Role#isPackageQualified(String, Context)
*/
@Nullable
- default Boolean isPackageQualified(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {
+ default Boolean isPackageQualifiedAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
return null;
}
@@ -87,15 +91,16 @@ public interface RoleBehavior {
}
/**
- * @see Role#grant(String, boolean, boolean, boolean, Context)
+ * @see Role#grantAsUser(String, boolean, boolean, UserHandle, Context)
*/
- default void grant(@NonNull Role role, @NonNull String packageName, @NonNull Context context) {}
+ default void grantAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {}
/**
- * @see Role#revoke(String, boolean, boolean, Context)
+ * @see Role#revokeAsUser(String, boolean, boolean, UserHandle, Context)
*/
- default void revoke(@NonNull Role role, @NonNull String packageName,
- @NonNull Context context) {}
+ default void revokeAsUser(@NonNull Role role, @NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {}
/**
* @see Role#onHolderSelectedAsUser(String, UserHandle, Context)
@@ -108,4 +113,34 @@ public interface RoleBehavior {
*/
default void onHolderChangedAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {}
+
+ /**
+ * Check whether this role should be visible to user.
+ *
+ * @param role the role to check for
+ * @param user the user to check for
+ * @param context the `Context` to retrieve system services
+ *
+ * @return whether this role should be visible to user
+ */
+ default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return true;
+ }
+
+ /**
+ * Check whether a qualifying application should be visible to user.
+ *
+ * @param role the role to check for
+ * @param applicationInfo the {@link ApplicationInfo} for the application
+ * @param user the user for the application
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return whether the qualifying application should be visible to user
+ */
+ default boolean isApplicationVisibleAsUser(@NonNull Role role,
+ @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
+ @NonNull Context context) {
+ return true;
+ }
}
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..8c97bffc3 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
@@ -21,8 +21,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
+import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Build;
+import android.os.Process;
+import android.permission.flags.Flags;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -31,7 +34,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import com.android.role.controller.behavior.BrowserRoleBehavior;
+import com.android.role.controller.util.ResourceUtils;
import org.xmlpull.v1.XmlPullParserException;
@@ -88,6 +93,7 @@ public class RoleParser {
private static final String ATTRIBUTE_LABEL = "label";
private static final String ATTRIBUTE_MAX_SDK_VERSION = "maxSdkVersion";
private static final String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion";
+ private static final String ATTRIBUTE_ONLY_GRANT_WHEN_ADDED = "onlyGrantWhenAdded";
private static final String ATTRIBUTE_OVERRIDE_USER_WHEN_GRANTING = "overrideUserWhenGranting";
private static final String ATTRIBUTE_QUERY_FLAGS = "queryFlags";
private static final String ATTRIBUTE_REQUEST_TITLE = "requestTitle";
@@ -102,6 +108,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";
@@ -149,7 +156,7 @@ public class RoleParser {
*/
@NonNull
public ArrayMap<String, Role> parse() {
- try (XmlResourceParser parser = sGetRolesXml.apply(mContext)) {
+ try (XmlResourceParser parser = getRolesXml()) {
Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseXml(parser);
if (xml == null) {
return new ArrayMap<>();
@@ -164,6 +171,20 @@ public class RoleParser {
}
}
+ /**
+ * Retrieves the roles.xml resource from a context
+ */
+ private XmlResourceParser getRolesXml() {
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ Resources resources = ResourceUtils.getPermissionControllerResources(mContext);
+ int resourceId = resources.getIdentifier("roles", "xml",
+ ResourceUtils.RESOURCE_PACKAGE_NAME_PERMISSION_CONTROLLER);
+ return resources.getXml(resourceId);
+ } else {
+ return sGetRolesXml.apply(mContext);
+ }
+ }
+
@Nullable
private Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> parseXml(
@NonNull XmlResourceParser parser) throws IOException, XmlPullParserException {
@@ -252,6 +273,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 +295,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 +321,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
@@ -373,6 +406,9 @@ public class RoleParser {
return null;
}
+ boolean onlyGrantWhenAdded = getAttributeBooleanValue(parser,
+ ATTRIBUTE_ONLY_GRANT_WHEN_ADDED, false);
+
boolean overrideUserWhenGranting = getAttributeBooleanValue(parser,
ATTRIBUTE_OVERRIDE_USER_WHEN_GRANTING, false);
@@ -500,10 +536,11 @@ public class RoleParser {
}
return new Role(name, allowBypassingQualification, behavior, defaultHoldersResourceName,
descriptionResource, exclusive, fallBackToDefaultHolder, labelResource,
- maxSdkVersion, minSdkVersion, overrideUserWhenGranting, requestDescriptionResource,
- requestTitleResource, requestable, searchKeywordsResource, shortLabelResource,
- showNone, statik, systemOnly, visible, requiredComponents, permissions,
- appOpPermissions, appOps, preferredActivities, uiBehaviorName);
+ maxSdkVersion, minSdkVersion, onlyGrantWhenAdded, overrideUserWhenGranting,
+ requestDescriptionResource, requestTitleResource, requestable,
+ searchKeywordsResource, shortLabelResource, showNone, statik, systemOnly, visible,
+ requiredComponents, permissions, appOpPermissions, appOps, preferredActivities,
+ uiBehaviorName);
}
@NonNull
@@ -738,13 +775,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: {
@@ -828,6 +881,8 @@ public class RoleParser {
throwOrLogMessage("Invalid value for \"maxTargetSdkVersion\": "
+ maxTargetSdkVersion);
}
+ int minSdkVersion = getAttributeIntValue(parser,
+ ATTRIBUTE_MIN_SDK_VERSION, Build.VERSION_CODES.BASE);
String modeName = requireAttributeValue(parser, ATTRIBUTE_MODE, TAG_APP_OP);
if (modeName == null) {
continue;
@@ -838,7 +893,7 @@ public class RoleParser {
continue;
}
int mode = sModeNameToMode.valueAt(modeIndex);
- AppOp appOp = new AppOp(name, maxTargetSdkVersion, mode);
+ AppOp appOp = new AppOp(name, maxTargetSdkVersion, minSdkVersion, mode);
appOps.add(appOp);
} else {
throwOrLogForUnknownTag(parser);
@@ -1110,7 +1165,7 @@ public class RoleParser {
}
private void validatePermission(@NonNull Permission permission) {
- if (!permission.isAvailable()) {
+ if (!permission.isAvailableAsUser(Process.myUserHandle(), mContext)) {
return;
}
validatePermission(permission.getName(), true);
@@ -1146,7 +1201,7 @@ public class RoleParser {
}
private void validateAppOpPermission(@NonNull Permission appOpPermission) {
- if (!appOpPermission.isAvailable()) {
+ if (!appOpPermission.isAvailableAsUser(Process.myUserHandle(), mContext)) {
return;
}
validateAppOpPermission(appOpPermission.getName());
@@ -1168,6 +1223,9 @@ public class RoleParser {
}
private void validateAppOp(@NonNull AppOp appOp) {
+ if (!appOp.isAvailableBySdkVersion()) {
+ return;
+ }
// This throws IllegalArgumentException if app op is unknown.
String permission = AppOpsManager.opToPermission(appOp.getName());
if (permission != null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java b/PermissionController/role-controller/java/com/android/role/controller/model/VisibilityMixin.java
index 90cda72ca..fdfb45143 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/model/VisibilityMixin.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/VisibilityMixin.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.role.model;
+package com.android.role.controller.model;
import android.content.Context;
import android.content.res.Resources;
@@ -23,8 +23,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
-import com.android.role.controller.model.Role;
-import com.android.role.controller.model.RoleBehavior;
+import com.android.role.controller.util.ResourceUtils;
/**
* Mixin for {@link RoleBehavior#isVisibleAsUser(Role, UserHandle, Context)} that returns whether
@@ -37,11 +36,26 @@ public class VisibilityMixin {
private VisibilityMixin() {}
/**
- * @see Role#isVisibleAsUser(UserHandle, Context)
+ * Get the boolean resource value that represents whether a role is visible to the user.
+ *
+ * @param resourceName the name of the resource
+ * @param isPermissionControllerResource if {@code true}, and if the current SDK level is at
+ * least V, get the resource from a PermissionController context for the given user.
+ * Otherwise, get the resource the provided context.
+ * @param user the user to get the PermissionController context for
+ * @param context the `Context` to retrieve the resource (and system services)
+ *
+ * @return whether this role should be visible to user
*/
- public static boolean isVisible(@NonNull String resourceName, @NonNull Context context) {
- Resources resources = context.getResources();
- int resourceId = resources.getIdentifier(resourceName, "bool", "android");
+ public static boolean isVisible(@NonNull String resourceName,
+ boolean isPermissionControllerResource, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Resources resources = isPermissionControllerResource
+ ? ResourceUtils.getPermissionControllerResources(context) : context.getResources();
+ String packageName = isPermissionControllerResource
+ ? ResourceUtils.RESOURCE_PACKAGE_NAME_PERMISSION_CONTROLLER : "android";
+
+ int resourceId = resources.getIdentifier(resourceName, "bool", packageName);
if (resourceId == 0) {
Log.w(LOG_TAG, "Cannot find resource for visibility: " + resourceName);
return true;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
index c1c7da46a..bc7562c11 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.role.service;
+package com.android.role.controller.service;
import android.app.role.RoleControllerService;
import android.app.role.RoleManager;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.os.UserHandle;
@@ -28,11 +29,12 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
-import com.android.permissioncontroller.permission.utils.CollectionUtils;
-import com.android.permissioncontroller.role.utils.PackageUtils;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
+import com.android.role.controller.util.CollectionUtils;
+import com.android.role.controller.util.LegacyRoleFallbackEnabledUtils;
+import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.UserUtils;
import java.util.ArrayList;
import java.util.List;
@@ -47,24 +49,40 @@ public class RoleControllerServiceImpl extends RoleControllerService {
private static final boolean DEBUG = false;
- private RoleManager mRoleManager;
+
+ private UserHandle mUser;
+ private Context mContext;
+ private RoleManager mUserRoleManager;
+
+ public RoleControllerServiceImpl() {}
+
+ public RoleControllerServiceImpl(@NonNull UserHandle user, @NonNull Context context) {
+ init(user, context);
+ }
@Override
public void onCreate() {
super.onCreate();
- mRoleManager = getSystemService(RoleManager.class);
+ init(Process.myUserHandle(), this);
+ }
+
+ private void init(@NonNull UserHandle user, @NonNull Context context) {
+ mUser = user;
+ mContext = context;
+ Context userContext = UserUtils.getUserContext(context, user);
+ mUserRoleManager = userContext.getSystemService(RoleManager.class);
}
@Override
@WorkerThread
public boolean onGrantDefaultRoles() {
if (DEBUG) {
- Log.i(LOG_TAG, "Granting default roles, user: " + UserHandle.myUserId());
+ Log.i(LOG_TAG, "Granting default roles, user: " + mUser.myUserId());
}
// Gather the available roles for current user.
- ArrayMap<String, Role> roleMap = Roles.get(this);
+ ArrayMap<String, Role> roleMap = Roles.get(mContext);
List<Role> roles = new ArrayList<>();
List<String> roleNames = new ArrayList<>();
ArraySet<String> addedRoleNames = new ArraySet<>();
@@ -72,13 +90,13 @@ public class RoleControllerServiceImpl extends RoleControllerService {
for (int i = 0; i < roleMapSize; i++) {
Role role = roleMap.valueAt(i);
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(mUser, mContext)) {
continue;
}
roles.add(role);
String roleName = role.getName();
roleNames.add(roleName);
- if (!mRoleManager.isRoleAvailable(roleName)) {
+ if (!mUserRoleManager.isRoleAvailable(roleName)) {
addedRoleNames.add(roleName);
}
}
@@ -86,14 +104,14 @@ public class RoleControllerServiceImpl extends RoleControllerService {
// TODO: Clean up holders of roles that will be removed.
// Set the available role names in RoleManager.
- mRoleManager.setRoleNamesFromController(roleNames);
+ mUserRoleManager.setRoleNamesFromController(roleNames);
int addedRoleNamesSize = addedRoleNames.size();
for (int i = 0; i < addedRoleNamesSize; i++) {
String roleName = addedRoleNames.valueAt(i);
Role role = roleMap.get(roleName);
- role.onRoleAdded(this);
+ role.onRoleAddedAsUser(mUser, mContext);
}
// Go through the holders of all roles.
@@ -105,18 +123,20 @@ public class RoleControllerServiceImpl extends RoleControllerService {
// For each of the current holders, check if it is still qualified, redo grant if so, or
// remove it otherwise.
- List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
+ List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
int currentPackageNamesSize = currentPackageNames.size();
for (int currentPackageNamesIndex = 0;
currentPackageNamesIndex < currentPackageNamesSize;
currentPackageNamesIndex++) {
String packageName = currentPackageNames.get(currentPackageNamesIndex);
- if (role.isPackageQualified(packageName, this)) {
- // We should not override user set or fixed permissions because we are only
- // redoing the grant here. Otherwise, user won't be able to revoke permissions
- // granted by role.
- addRoleHolderInternal(role, packageName, false, false, true);
+ if (role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
+ if (!role.shouldOnlyGrantWhenAdded()) {
+ // We should not override user set or fixed permissions because we are only
+ // redoing the grant here. Otherwise, user won't be able to revoke
+ // permissions granted by role.
+ addRoleHolderInternal(role, packageName, false, false, true);
+ }
} else {
Log.i(LOG_TAG, "Removing package that no longer qualifies for the role,"
+ " package: " + packageName + ", role: " + roleName);
@@ -126,17 +146,17 @@ public class RoleControllerServiceImpl extends RoleControllerService {
// If there is no holder for a role now, or the role is static, we need to add default
// or fallback holders, if any.
- currentPackageNames = mRoleManager.getRoleHolders(roleName);
+ currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
currentPackageNamesSize = currentPackageNames.size();
boolean isStaticRole = role.isStatic();
if (currentPackageNamesSize == 0 || isStaticRole) {
List<String> packageNamesToAdd = null;
if (addedRoleNames.contains(roleName) || isStaticRole) {
- packageNamesToAdd = role.getDefaultHolders(this);
+ packageNamesToAdd = role.getDefaultHoldersAsUser(mUser, mContext);
}
if (packageNamesToAdd == null || packageNamesToAdd.isEmpty()) {
- packageNamesToAdd = CollectionUtils.singletonOrEmpty(role.getFallbackHolder(
- this));
+ packageNamesToAdd = CollectionUtils.singletonOrEmpty(
+ role.getFallbackHolderAsUser(mUser, mContext));
}
int packageNamesToAddSize = packageNamesToAdd.size();
@@ -149,7 +169,7 @@ public class RoleControllerServiceImpl extends RoleControllerService {
// static roles.
continue;
}
- if (!role.isPackageQualified(packageName, this)) {
+ if (!role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
Log.e(LOG_TAG, "Default/fallback role holder package doesn't qualify for"
+ " the role, package: " + packageName + ", role: " + roleName);
continue;
@@ -165,7 +185,7 @@ public class RoleControllerServiceImpl extends RoleControllerService {
}
// Ensure that an exclusive role has at most one holder.
- currentPackageNames = mRoleManager.getRoleHolders(roleName);
+ currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
currentPackageNamesSize = currentPackageNames.size();
if (role.isExclusive() && currentPackageNamesSize > 1) {
Log.w(LOG_TAG, "Multiple packages holding an exclusive role, role: "
@@ -194,17 +214,17 @@ public class RoleControllerServiceImpl extends RoleControllerService {
return false;
}
- Role role = Roles.get(this).get(roleName);
+ Role role = Roles.get(mContext).get(roleName);
if (role == null) {
Log.e(LOG_TAG, "Unknown role: " + roleName);
return false;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(mUser, mContext)) {
Log.e(LOG_TAG, "Role is unavailable: " + roleName);
return false;
}
- if (!role.isPackageQualified(packageName, this)) {
+ if (!role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
Log.e(LOG_TAG, "Package does not qualify for the role, package: " + packageName
+ ", role: " + roleName);
return false;
@@ -212,7 +232,7 @@ public class RoleControllerServiceImpl extends RoleControllerService {
boolean added = false;
if (role.isExclusive()) {
- List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
+ List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
int currentPackageNamesSize = currentPackageNames.size();
for (int i = 0; i < currentPackageNamesSize; i++) {
String currentPackageName = currentPackageNames.get(i);
@@ -239,8 +259,8 @@ public class RoleControllerServiceImpl extends RoleControllerService {
return false;
}
- role.onHolderAddedAsUser(packageName, Process.myUserHandle(), this);
- role.onHolderChangedAsUser(Process.myUserHandle(), this);
+ role.onHolderAddedAsUser(packageName, mUser, mContext);
+ role.onHolderChangedAsUser(mUser, mContext);
return true;
}
@@ -253,12 +273,12 @@ public class RoleControllerServiceImpl extends RoleControllerService {
return false;
}
- Role role = Roles.get(this).get(roleName);
+ Role role = Roles.get(mContext).get(roleName);
if (role == null) {
Log.e(LOG_TAG, "Unknown role: " + roleName);
return false;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(mUser, mContext)) {
Log.e(LOG_TAG, "Role is unavailable: " + roleName);
return false;
}
@@ -275,7 +295,7 @@ public class RoleControllerServiceImpl extends RoleControllerService {
return false;
}
- role.onHolderChangedAsUser(Process.myUserHandle(), this);
+ role.onHolderChangedAsUser(mUser, mContext);
return true;
}
@@ -287,12 +307,12 @@ public class RoleControllerServiceImpl extends RoleControllerService {
return false;
}
- Role role = Roles.get(this).get(roleName);
+ Role role = Roles.get(mContext).get(roleName);
if (role == null) {
Log.e(LOG_TAG, "Unknown role: " + roleName);
return false;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(mUser, mContext)) {
Log.e(LOG_TAG, "Role is unavailable: " + roleName);
return false;
}
@@ -309,7 +329,7 @@ public class RoleControllerServiceImpl extends RoleControllerService {
return false;
}
- role.onHolderChangedAsUser(Process.myUserHandle(), this);
+ role.onHolderChangedAsUser(mUser, mContext);
return true;
}
@@ -323,11 +343,11 @@ public class RoleControllerServiceImpl extends RoleControllerService {
@WorkerThread
private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
boolean dontKillApp, boolean overrideUser, boolean added) {
- role.grant(packageName, dontKillApp, overrideUser, this);
+ role.grantAsUser(packageName, dontKillApp, overrideUser, mUser, mContext);
String roleName = role.getName();
if (!added) {
- added = mRoleManager.addRoleHolderFromController(roleName, packageName);
+ added = mUserRoleManager.addRoleHolderFromController(roleName, packageName);
}
if (!added) {
Log.e(LOG_TAG, "Failed to add role holder in RoleManager, package: " + packageName
@@ -339,17 +359,18 @@ public class RoleControllerServiceImpl extends RoleControllerService {
@WorkerThread
private boolean removeRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
boolean dontKillApp) {
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this);
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
+ mUser, mContext);
if (applicationInfo == null) {
Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
}
if (applicationInfo != null) {
- role.revoke(packageName, dontKillApp, false, this);
+ role.revokeAsUser(packageName, dontKillApp, false, mUser, mContext);
}
String roleName = role.getName();
- boolean removed = mRoleManager.removeRoleHolderFromController(roleName, packageName);
+ boolean removed = mUserRoleManager.removeRoleHolderFromController(roleName, packageName);
if (!removed) {
Log.e(LOG_TAG, "Failed to remove role holder in RoleManager," + " package: "
+ packageName + ", role: " + roleName);
@@ -360,7 +381,7 @@ public class RoleControllerServiceImpl extends RoleControllerService {
@WorkerThread
private boolean clearRoleHoldersInternal(@NonNull Role role, boolean dontKillApp) {
String roleName = role.getName();
- List<String> packageNames = mRoleManager.getRoleHolders(roleName);
+ List<String> packageNames = mUserRoleManager.getRoleHolders(roleName);
boolean cleared = true;
int packageNamesSize = packageNames.size();
@@ -381,17 +402,17 @@ public class RoleControllerServiceImpl extends RoleControllerService {
@WorkerThread
private boolean addFallbackRoleHolderMaybe(@NonNull Role role) {
String roleName = role.getName();
- List<String> currentPackageNames = mRoleManager.getRoleHolders(roleName);
+ List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
if (!currentPackageNames.isEmpty()) {
return true;
}
- String fallbackPackageName = role.getFallbackHolder(this);
+ String fallbackPackageName = role.getFallbackHolderAsUser(mUser, mContext);
if (fallbackPackageName == null) {
return true;
}
- if (!role.isPackageQualified(fallbackPackageName, this)) {
+ if (!role.isPackageQualifiedAsUser(fallbackPackageName, mUser, mContext)) {
Log.e(LOG_TAG, "Fallback role holder package doesn't qualify for the role, package: "
+ fallbackPackageName + ", role: " + roleName);
return false;
@@ -418,19 +439,20 @@ public class RoleControllerServiceImpl extends RoleControllerService {
@Override
public boolean onIsApplicationVisibleForRole(@NonNull String roleName,
@NonNull String packageName) {
- Role role = Roles.get(this).get(roleName);
+ Role role = Roles.get(mContext).get(roleName);
if (role == null) {
return false;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(mUser, mContext)) {
return false;
}
- if (!role.isPackageQualified(packageName, this)) {
+ if (!role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
return false;
}
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(packageName, this);
- if (applicationInfo == null || !RoleUiBehaviorUtils.isApplicationVisibleAsUser(role,
- applicationInfo, Process.myUserHandle(), this)) {
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
+ mUser, mContext);
+ if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo, mUser,
+ mContext)) {
return false;
}
return true;
@@ -438,17 +460,24 @@ public class RoleControllerServiceImpl extends RoleControllerService {
@Override
public boolean onIsRoleVisible(@NonNull String roleName) {
- Role role = Roles.get(this).get(roleName);
+ Role role = Roles.get(mContext).get(roleName);
if (role == null) {
return false;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(mUser, mContext)) {
return false;
}
- return RoleUiBehaviorUtils.isVisibleAsUser(role, Process.myUserHandle(), this);
+ return role.isVisibleAsUser(mUser, mContext);
}
+ @Override
+ @NonNull
+ public List<String> onGetLegacyFallbackDisabledRoles() {
+ return LegacyRoleFallbackEnabledUtils.getFallbackDisabledRoles(mUser, mContext);
+ }
+
+
private static boolean checkFlags(int flags, int allowedFlags) {
if ((flags & allowedFlags) != flags) {
Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags)
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/LegacyRoleFallbackEnabledUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/LegacyRoleFallbackEnabledUtils.java
new file mode 100644
index 000000000..5be10a26a
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/LegacyRoleFallbackEnabledUtils.java
@@ -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.role.controller.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LegacyRoleFallbackEnabledUtils {
+ /**
+ * Name of generic shared preferences file.
+ */
+ private static final String PREFERENCES_FILE = "preferences";
+
+ /**
+ * Key in the generic shared preferences that stores if the user manually selected the "none"
+ * role holder for a role.
+ */
+ private static final String IS_NONE_ROLE_HOLDER_SELECTED_KEY = "is_none_role_holder_selected:";
+
+ /**
+ * Get a device protected storage based shared preferences. Avoid storing sensitive data in it.
+ *
+ * @param context the context to get the shared preferences
+ * @return a device protected storage based shared preferences
+ */
+ @NonNull
+ @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static SharedPreferences getSharedPreferences(@NonNull UserHandle user,
+ @NonNull Context context) {
+ String packageName = context.getPackageManager().getPermissionControllerPackageName();
+ try {
+ context = context.createPackageContextAsUser(packageName, 0, user);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ if (!context.isDeviceProtectedStorage()) {
+ context = context.createDeviceProtectedStorageContext();
+ }
+ return context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Get all role names with fallback disabled, which means their "none" are set to true.
+ *
+ * @return A list of role names with fallback disabled.
+ */
+ public static List<String> getFallbackDisabledRoles(@NonNull UserHandle user,
+ @NonNull Context context) {
+ List<String> fallbackDisabledRoles = new ArrayList<>();
+ SharedPreferences sharedPreferences = getSharedPreferences(user, context);
+ for (String key : sharedPreferences.getAll().keySet()) {
+ if (key.startsWith(IS_NONE_ROLE_HOLDER_SELECTED_KEY)
+ && sharedPreferences.getBoolean(key, false)) {
+ String roleName = key.substring(IS_NONE_ROLE_HOLDER_SELECTED_KEY.length());
+ fallbackDisabledRoles.add(roleName);
+ }
+ }
+ return fallbackDisabledRoles;
+ }
+
+ /**
+ * Check whether the role has the fallback holder enabled.
+ *
+ * @return whether the "none" role holder is not selected
+ */
+ public static boolean isRoleFallbackEnabledAsUser(@NonNull String roleName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return !getSharedPreferences(user, context)
+ .getBoolean(IS_NONE_ROLE_HOLDER_SELECTED_KEY + roleName, false);
+ }
+
+ /**
+ * Set whether the role has fallback holder enabled.
+ */
+ public static void setRoleFallbackEnabledAsUser(@NonNull String roleName,
+ boolean fallbackEnabled, @NonNull UserHandle user, @NonNull Context context) {
+ String key = IS_NONE_ROLE_HOLDER_SELECTED_KEY + roleName;
+ if (fallbackEnabled) {
+ getSharedPreferences(user, context).edit().remove(key).apply();
+ } else {
+ getSharedPreferences(user, context).edit().putBoolean(key, true).apply();
+ }
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/NotificationUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/NotificationUtils.java
index 3b11d7d92..365c3b491 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/NotificationUtils.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/NotificationUtils.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.util.Log;
@@ -44,41 +45,45 @@ public final class NotificationUtils {
/**
* Grants the NotificationListener access.
*
- * @param context the {@code Context} to retrieve system services
* @param packageName the package name implements the NotificationListener
+ * @param user the user of the component
+ * @param context the {@code Context} to retrieve system services
*/
- public static void grantNotificationAccessForPackage(@NonNull Context context,
- @NonNull String packageName) {
- setNotificationGrantStateForPackage(context, packageName, true);
+ public static void grantNotificationAccessForPackageAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ setNotificationGrantStateForPackageAsUser(packageName, true, user, context);
}
/**
* Revokes the NotificationListener access.
*
- * @param context the {@code Context} to retrieve system services
* @param packageName the package name implements the NotificationListener
+ * @param user the user of the component
+ * @param context the {@code Context} to retrieve system services
*/
- public static void revokeNotificationAccessForPackage(@NonNull Context context,
- @NonNull String packageName) {
- setNotificationGrantStateForPackage(context, packageName, false);
+ public static void revokeNotificationAccessForPackageAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ setNotificationGrantStateForPackageAsUser(packageName, false, user, context);
}
- private static void setNotificationGrantStateForPackage(@NonNull Context context,
- @NonNull String packageName, boolean granted) {
+ private static void setNotificationGrantStateForPackageAsUser(@NonNull String packageName,
+ boolean granted, @NonNull UserHandle user, @NonNull Context context) {
List<ComponentName> notificationListenersForPackage =
- getNotificationListenersForPackage(packageName, context);
- NotificationManager notificationManager =
- context.getSystemService(NotificationManager.class);
+ getNotificationListenersForPackageAsUser(packageName, user, context);
+ Context userContext = UserUtils.getUserContext(context, user);
+ NotificationManager userNotificationManager =
+ userContext.getSystemService(NotificationManager.class);
for (ComponentName componentName : notificationListenersForPackage) {
- notificationManager.setNotificationListenerAccessGranted(
+ userNotificationManager.setNotificationListenerAccessGranted(
componentName, granted, false);
}
}
- private static List<ComponentName> getNotificationListenersForPackage(
- @NonNull String packageName, @NonNull Context context) {
- List<ResolveInfo> allListeners = context.getPackageManager().queryIntentServices(
+ private static List<ComponentName> getNotificationListenersForPackageAsUser(
+ @NonNull String packageName, @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ List<ResolveInfo> allListeners = userContext.getPackageManager().queryIntentServices(
new Intent(NotificationListenerService.SERVICE_INTERFACE).setPackage(packageName),
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
ArrayList<ComponentName> pkgListeners = new ArrayList<>();
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java
index 4b127ad10..cbffd451a 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java
@@ -36,19 +36,21 @@ public final class PackageUtils {
* Retrieve the {@link PackageInfo} of an application.
*
* @param packageName the package name of the application
- * @param extraFlags the extra flags to pass to {@link PackageManager#getPackageInfo(String,
- * int)}
- * @param context the {@code Context} to retrieve system services
- *
+ * @param extraFlags the extra flags to pass to {@link PackageManager#getPackageInfo(String,
+ * int)}
+ * @param user the user of the application
+ * @param context the {@code Context} to retrieve system services
* @return the {@link PackageInfo} of the application, or {@code null} if not found
*/
@Nullable
- public static PackageInfo getPackageInfo(@NonNull String packageName, int extraFlags,
- @NonNull Context context) {
- PackageManager packageManager = context.getPackageManager();
+ public static PackageInfo getPackageInfoAsUser(@NonNull String packageName, int extraFlags,
+ @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
try {
- return packageManager.getPackageInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | extraFlags);
+ return userPackageManager.getPackageInfo(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | extraFlags);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
@@ -58,12 +60,15 @@ public final class PackageUtils {
* Retrieve if a package is a system package.
*
* @param packageName the name of the package
+ * @param user the user of the package
* @param context the {@code Context} to retrieve system services
*
* @return whether the package is a system package
*/
- public static boolean isSystemPackage(@NonNull String packageName, @NonNull Context context) {
- return getPackageInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY, context) != null;
+ public static boolean isSystemPackageAsUser(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ return getPackageInfoAsUser(packageName, PackageManager.MATCH_SYSTEM_ONLY, user, context)
+ != null;
}
/**
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java
new file mode 100644
index 000000000..f8f12108a
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/ResourceUtils.java
@@ -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.role.controller.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.permission.flags.Flags;
+
+import androidx.annotation.NonNull;
+
+import com.android.modules.utils.build.SdkLevel;
+
+public class ResourceUtils {
+
+ private ResourceUtils() {}
+
+ public static String RESOURCE_PACKAGE_NAME_PERMISSION_CONTROLLER =
+ "com.android.permissioncontroller";
+
+ /**
+ * Get a {@link Resources} object to be used to access PermissionController resources.
+ */
+ @NonNull
+ public static Resources getPermissionControllerResources(@NonNull Context context) {
+ return getPermissionControllerContext(context).getResources();
+ }
+
+ @NonNull
+ private static Context getPermissionControllerContext(@NonNull Context context) {
+ if (!SdkLevel.isAtLeastV() || !Flags.systemServerRoleControllerEnabled()) {
+ // We don't have the getPermissionControllerPackageName() API below V,
+ // but role controller always runs in PermissionController below V.
+ return context;
+ }
+ String packageName = context.getPackageManager().getPermissionControllerPackageName();
+ try {
+ return context.createPackageContext(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Cannot create PermissionController context", e);
+ }
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/RoleManagerCompat.java b/PermissionController/role-controller/java/com/android/role/controller/util/RoleManagerCompat.java
index 8a6fd579c..ec63528d1 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/RoleManagerCompat.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/RoleManagerCompat.java
@@ -18,8 +18,8 @@ package com.android.role.controller.util;
import android.app.role.RoleManager;
import android.content.Context;
-import android.content.SharedPreferences;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import androidx.annotation.NonNull;
@@ -31,16 +31,7 @@ import com.android.role.controller.model.Role;
*/
public class RoleManagerCompat {
- /**
- * Key in the generic shared preferences that stores if the user manually selected the "none"
- * role holder for a role.
- */
- private static final String IS_NONE_ROLE_HOLDER_SELECTED_KEY = "is_none_role_holder_selected:";
- /**
- * Name of generic shared preferences file.
- */
- private static final String PREFERENCES_FILE = "preferences";
private RoleManagerCompat() {}
@@ -56,47 +47,34 @@ public class RoleManagerCompat {
}
/**
- * Get a device protected storage based shared preferences. Avoid storing sensitive data in it.
- *
- * @param context the context to get the shared preferences
- * @return a device protected storage based shared preferences
- */
- @NonNull
- private static SharedPreferences getDeviceProtectedSharedPreferences(@NonNull Context context) {
- if (!context.isDeviceProtectedStorage()) {
- context = context.createDeviceProtectedStorageContext();
- }
- return context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
- }
-
- /**
* Check whether the role has the fallback holder enabled.
*
* @return whether the "none" role holder is not selected
*/
public static boolean isRoleFallbackEnabledAsUser(@NonNull Role role, @NonNull UserHandle user,
@NonNull Context context) {
- Context userContext = UserUtils.getUserContext(context, user);
- boolean isNoneHolderSelected = getDeviceProtectedSharedPreferences(userContext)
- .getBoolean(IS_NONE_ROLE_HOLDER_SELECTED_KEY + role.getName(), false);
- return !isNoneHolderSelected;
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ return userRoleManager.isRoleFallbackEnabled(role.getName());
+ } else {
+ return LegacyRoleFallbackEnabledUtils.isRoleFallbackEnabledAsUser(role.getName(), user,
+ context);
+ }
}
/**
* Set whether the role has fallback holder enabled.
- *
*/
public static void setRoleFallbackEnabledAsUser(@NonNull Role role,
boolean fallbackEnabled, @NonNull UserHandle user, @NonNull Context context) {
- Context userContext = UserUtils.getUserContext(context, user);
- if (fallbackEnabled) {
- getDeviceProtectedSharedPreferences(userContext).edit()
- .remove(IS_NONE_ROLE_HOLDER_SELECTED_KEY + role.getName())
- .apply();
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ RoleManager userRoleManager = userContext.getSystemService(RoleManager.class);
+ userRoleManager.setRoleFallbackEnabled(role.getName(), fallbackEnabled);
} else {
- getDeviceProtectedSharedPreferences(userContext).edit()
- .putBoolean(IS_NONE_ROLE_HOLDER_SELECTED_KEY + role.getName(), true)
- .apply();
+ LegacyRoleFallbackEnabledUtils.setRoleFallbackEnabledAsUser(role.getName(),
+ fallbackEnabled, user, context);
}
}
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
index ac3b681a8..1b6926ef8 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/UserUtils.java
@@ -18,12 +18,15 @@ package com.android.role.controller.util;
import android.content.Context;
import android.os.Build;
+import android.os.Flags;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import androidx.annotation.NonNull;
+import com.android.modules.utils.build.SdkLevel;
+
/** Utility class to deal with Android users. */
public final class UserUtils {
@@ -37,7 +40,13 @@ public final class UserUtils {
* @return whether the user is a profile
*/
public static boolean isProfile(@NonNull UserHandle user, @NonNull Context context) {
- return isManagedProfile(user, context) || isCloneProfile(user, context);
+ if (SdkLevel.isAtLeastV()) {
+ Context userContext = getUserContext(context, user);
+ UserManager userUserManager = userContext.getSystemService(UserManager.class);
+ return userUserManager.isProfile();
+ } else {
+ return isManagedProfile(user, context) || isCloneProfile(user, context);
+ }
}
/**
@@ -70,6 +79,23 @@ public final class UserUtils {
}
/**
+ * Check whether a user is a private profile.
+ *
+ * @param user the user to check
+ * @param context the {@code Context} to retrieve system services
+ * @return whether the user is a private profile. Private profiles are
+ * allowed from Android V+ only, so this method will return false on Sdk levels below that.
+ */
+ public static boolean isPrivateProfile(@NonNull UserHandle user, @NonNull Context context) {
+ if (!SdkLevel.isAtLeastV() || !Flags.allowPrivateProfile()) {
+ return false;
+ }
+ Context userContext = getUserContext(context, user);
+ UserManager userUserManager = userContext.getSystemService(UserManager.class);
+ return userUserManager.isPrivateProfile();
+ }
+
+ /**
* Create a context for a user.
*
* @param context The context to clone
diff --git a/PermissionController/role-controller/lint-baseline.xml b/PermissionController/role-controller/lint-baseline.xml
index e7c119f3b..894dc1834 100644
--- a/PermissionController/role-controller/lint-baseline.xml
+++ b/PermissionController/role-controller/lint-baseline.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
id="NewApi"
message="Call requires API level 31 (current min is 30): `android.app.NotificationManager#setNotificationListenerAccessGranted`"
- errorLine1=" notificationManager.setNotificationListenerAccessGranted("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ errorLine1=" userNotificationManager.setNotificationListenerAccessGranted("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/modules/Permission/PermissionController/role-controller/java/com/android/role/controller/util/NotificationUtils.java"
- line="74"
- column="33"/>
+ line="78"
+ column="37"/>
</issue>
</issues> \ No newline at end of file
diff --git a/PermissionController/src/com/android/permissioncontroller/Constants.java b/PermissionController/src/com/android/permissioncontroller/Constants.java
index 58b62dc54..47ec9cabb 100644
--- a/PermissionController/src/com/android/permissioncontroller/Constants.java
+++ b/PermissionController/src/com/android/permissioncontroller/Constants.java
@@ -276,6 +276,12 @@ public class Constants {
"com.android.permissioncontroller.extra.SESSION_ID";
/**
+ * Intent extra used to pass if the restriction dialog is triggered in-app.
+ */
+ public static final String EXTRA_IS_ECM_IN_APP =
+ "com.android.permissincontroller.extra.IS_ECM_IN_APP";
+
+ /**
* Intent extra used to pass privacy source details to safety center.
*/
public static final String EXTRA_PRIVACY_SOURCE =
@@ -320,6 +326,17 @@ public class Constants {
*/
public static final String UNUSED_APPS_SAFETY_CENTER_SEE_UNUSED_APPS_ID = "see_unused_apps";
+ /**
+ * Fallback Settings package name
+ */
+ public static final String SETTINGS_PACKAGE_NAME_FALLBACK = "com.android.settings";
+
+ /**
+ * Extra launcher icon for notification
+ */
+ public static final String NOTIFICATION_EXTRA_USE_LAUNCHER_ICON =
+ "com.android.car.notification.EXTRA_USE_LAUNCHER_ICON";
+
// TODO(b/231624295) add to API
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO =
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/PermissionControllerApplication.java b/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java
index 68495ce1e..50da28149 100644
--- a/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java
+++ b/PermissionController/src/com/android/permissioncontroller/PermissionControllerApplication.java
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.Process;
import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
@@ -32,7 +33,6 @@ import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.privacysources.SafetyCenterAccessibilityListener;
import com.android.permissioncontroller.role.model.RoleParserInitializer;
import com.android.permissioncontroller.role.ui.SpecialAppAccessListActivity;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -71,7 +71,8 @@ public final class PermissionControllerApplication extends Application {
for (int i = 0; i < rolesSize; i++) {
Role role = roles.valueAt(i);
- if (!role.isAvailable(this) || !RoleUiBehaviorUtils.isVisible(role, this)) {
+ if (!role.isAvailableAsUser(Process.myUserHandle(), this)
+ || !role.isVisibleAsUser(Process.myUserHandle(), this)) {
continue;
}
if (!role.isExclusive()) {
diff --git a/PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/PackageAppOpUsageModel.kt b/PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/PackageAppOpUsageModel.kt
new file mode 100644
index 000000000..1820d01ae
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/appops/data/model/v31/PackageAppOpUsageModel.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.appops.data.model.v31
+
+/**
+ * Collection of app op usages for a package and user. App op usage represent private data access
+ * (i.e. location, contact access) by the app/package.
+ */
+data class PackageAppOpUsageModel(
+ val packageName: String,
+ val usages: List<AppOpUsageModel>,
+ val userId: Int
+) {
+ /** Data class representing an app op and the recent access time by an app. */
+ data class AppOpUsageModel(
+ val appOpName: String,
+ /** Milliseconds since the epoch */
+ val lastAccessTimestampMillis: Long,
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt b/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt
new file mode 100644
index 000000000..a8ff35d91
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/appops/data/repository/v31/AppOpRepository.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.appops.data.repository.v31
+
+import android.app.AppOpsManager
+import android.app.Application
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.permission.flags.Flags
+import android.util.Log
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel
+import com.android.permissioncontroller.permission.data.PackageBroadcastReceiver
+import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import kotlin.concurrent.Volatile
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * This repository encapsulate app op data (i.e. app op usage, app op mode, historical ops etc.)
+ * exposed by [AppOpsManager].
+ */
+interface AppOpRepository {
+ /**
+ * A flow/stream of package app ops, these app ops are processed to show the usage statistics in
+ * the privacy dashboard.
+ *
+ * @see AppOpsManager.getPackagesForOps
+ */
+ val packageAppOpsUsages: Flow<List<PackageAppOpUsageModel>>
+
+ companion object {
+ @Volatile private var instance: AppOpRepository? = null
+
+ fun getInstance(
+ application: Application,
+ permissionRepository: PermissionRepository
+ ): AppOpRepository =
+ instance
+ ?: synchronized(this) {
+ AppOpRepositoryImpl(application, permissionRepository).also { instance = it }
+ }
+ }
+}
+
+class AppOpRepositoryImpl(
+ application: Application,
+ private val permissionRepository: PermissionRepository,
+ private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
+) : AppOpRepository {
+ private val appOpsManager =
+ checkNotNull(application.getSystemService(AppOpsManager::class.java))
+ private val packageManager = application.packageManager
+
+ private val appOpNames = getPrivacyDashboardAppOpNames()
+
+ override val packageAppOpsUsages by lazy {
+ callbackFlow {
+ send(getPackageOps())
+
+ // Suppress OnOpNotedListener lint error, startWatchingNoted is behind sdk check.
+ @SuppressWarnings("NewApi")
+ val callback =
+ object :
+ PackageManager.OnPermissionsChangedListener,
+ PackageBroadcastReceiver.PackageBroadcastListener,
+ AppOpsManager.OnOpActiveChangedListener,
+ AppOpsManager.OnOpNotedListener,
+ AppOpsManager.OnOpChangedListener {
+ override fun onPermissionsChanged(uid: Int) {
+ sendUpdate()
+ }
+
+ override fun onOpChanged(op: String?, packageName: String?) {
+ sendUpdate()
+ }
+
+ override fun onPackageUpdate(packageName: String) {
+ sendUpdate()
+ }
+
+ override fun onOpActiveChanged(
+ op: String,
+ uid: Int,
+ packageName: String,
+ active: Boolean
+ ) {
+ sendUpdate()
+ }
+
+ override fun onOpNoted(
+ op: String,
+ uid: Int,
+ packageName: String,
+ attributionTag: String?,
+ flags: Int,
+ result: Int
+ ) {
+ sendUpdate()
+ }
+
+ fun sendUpdate() {
+ trySend(getPackageOps())
+ }
+ }
+
+ packageManager.addOnPermissionsChangeListener(callback)
+ PackageBroadcastReceiver.addAllCallback(callback)
+ appOpNames.forEach { opName ->
+ // TODO(b/262035952): We watch each active op individually as
+ // startWatchingActive only registers the callback if all ops are valid.
+ // Fix this behavior so if one op is invalid it doesn't affect the other ops.
+ try {
+ appOpsManager.startWatchingActive(arrayOf(opName), { it.run() }, callback)
+ } catch (ignored: IllegalArgumentException) {
+ // Older builds may not support all requested app ops.
+ }
+
+ try {
+ appOpsManager.startWatchingMode(opName, /* all packages */ null, callback)
+ } catch (ignored: IllegalArgumentException) {
+ // Older builds may not support all requested app ops.
+ }
+
+ if (SdkLevel.isAtLeastU()) {
+ try {
+ appOpsManager.startWatchingNoted(arrayOf(opName), callback)
+ } catch (ignored: IllegalArgumentException) {
+ // Older builds may not support all requested app ops.
+ }
+ }
+ }
+
+ awaitClose {
+ packageManager.removeOnPermissionsChangeListener(callback)
+ PackageBroadcastReceiver.removeAllCallback(callback)
+ appOpsManager.stopWatchingActive(callback)
+ appOpsManager.stopWatchingMode(callback)
+ if (SdkLevel.isAtLeastU()) {
+ appOpsManager.stopWatchingNoted(callback)
+ }
+ }
+ }
+ .flowOn(dispatcher)
+ }
+
+ private fun getPackageOps(): List<PackageAppOpUsageModel> {
+ return try {
+ appOpsManager.getPackagesForOps(appOpNames.toTypedArray())
+ } catch (e: NullPointerException) {
+ Log.w(LOG_TAG, "App ops not recognized, app ops list: $appOpNames")
+ // Older builds may not support all requested app ops.
+ emptyList()
+ }
+ .map { packageOps ->
+ PackageAppOpUsageModel(
+ packageOps.packageName,
+ packageOps.ops.map { opEntry ->
+ AppOpUsageModel(
+ opEntry.opStr,
+ opEntry.getLastAccessTime(OPS_LAST_ACCESS_FLAGS)
+ )
+ },
+ UserHandle.getUserHandleForUid(packageOps.uid).identifier
+ )
+ }
+ }
+
+ private fun getPrivacyDashboardAppOpNames(): Set<String> {
+ val permissionGroups = permissionRepository.getPermissionGroupsForPrivacyDashboard()
+ val opNames = mutableSetOf<String>()
+ for (permissionGroup in permissionGroups) {
+ val permissionNames =
+ PermissionMapping.getPlatformPermissionNamesOfGroup(permissionGroup)
+ for (permissionName in permissionNames) {
+ val opName = AppOpsManager.permissionToOp(permissionName) ?: continue
+ opNames.add(opName)
+ }
+ }
+
+ opNames.add(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE)
+ opNames.add(AppOpsManager.OPSTR_PHONE_CALL_CAMERA)
+ if (SdkLevel.isAtLeastT()) {
+ opNames.add(AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO)
+ }
+ if (SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled()) {
+ opNames.add(AppOpsManager.OPSTR_EMERGENCY_LOCATION)
+ }
+ return opNames
+ }
+
+ companion object {
+ private const val LOG_TAG = "AppOpUsageRepository"
+
+ private const val OPS_LAST_ACCESS_FLAGS =
+ AppOpsManager.OP_FLAG_SELF or
+ AppOpsManager.OP_FLAG_TRUSTED_PROXIED or
+ AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/auto/AutoSettingsFrameFragment.java b/PermissionController/src/com/android/permissioncontroller/auto/AutoSettingsFrameFragment.java
index 3b0e89b04..08e1b3560 100644
--- a/PermissionController/src/com/android/permissioncontroller/auto/AutoSettingsFrameFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/auto/AutoSettingsFrameFragment.java
@@ -27,6 +27,9 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.car.ui.FocusArea;
+import com.android.car.ui.R;
+import com.android.car.ui.baselayout.Insets;
import com.android.car.ui.preference.PreferenceFragment;
import com.android.car.ui.toolbar.MenuItem;
import com.android.car.ui.toolbar.ToolbarController;
@@ -56,6 +59,20 @@ public abstract class AutoSettingsFrameFragment extends PreferenceFragment {
return rootView;
}
+ @Override
+ public void onCarUiInsetsChanged(Insets insets) {
+ // don't allow scrolling behind the toolbar to be consistent with the rest of Settings
+ // reference UI. Scrolling behind toolbar also leads to flakier tests due to UI being
+ // visible but clicks are intercepted and dropped by the toolbar.
+ FocusArea focusArea = getView().findViewById(R.id.car_ui_focus_area);
+ focusArea.setHighlightPadding(
+ /* left= */ 0, /* top= */ 0, /* right= */ 0, /* bottom= */ 0);
+ focusArea.setBoundsOffset(/* left= */ 0, /* top= */ 0, /* right= */ 0, /* bottom= */ 0);
+ getView().setPadding(
+ insets.getLeft(), insets.getTop(), insets.getRight(), insets.getBottom());
+ getCarUiRecyclerView().setPadding(
+ /* left= */ 0, /* top= */ 0, /* right= */ 0, /* bottom= */ 0);
+ }
/** Sets the header text of this fragment. */
public void setHeaderLabel(CharSequence label) {
diff --git a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt
index b62f8d721..0ee0e0d01 100644
--- a/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/auto/DrivingDecisionReminderService.kt
@@ -24,16 +24,13 @@ import android.app.PendingIntent
import android.app.Service
import android.car.Car
import android.car.drivingstate.CarUxRestrictionsManager
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.os.Bundle
import android.os.IBinder
import android.os.Process
import android.os.UserHandle
import android.permission.PermissionManager
-import android.provider.Settings
import android.text.BidiFormatter
import androidx.annotation.VisibleForTesting
import com.android.permissioncontroller.Constants
@@ -55,9 +52,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,
@@ -72,7 +67,6 @@ class DrivingDecisionReminderService : Service() {
companion object {
private const val LOG_TAG = "DrivingDecisionReminderService"
- private const val SETTINGS_PACKAGE_NAME_FALLBACK = "com.android.settings"
const val EXTRA_PACKAGE_NAME = "package_name"
const val EXTRA_PERMISSION_GROUP = "permission_group"
@@ -109,27 +103,51 @@ 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()))
+ val restrictionsManager =
+ car.getCarManager(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()
}
}
+
+ fun cancelNotification(context: Context) {
+ val notificationManager = context.getSystemService(NotificationManager::class.java)!!
+ notificationManager.cancel(
+ DrivingDecisionReminderService::class.java.simpleName,
+ Constants.PERMISSION_DECISION_REMINDER_NOTIFICATION_ID
+ )
+ }
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -156,28 +174,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 +207,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 +226,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 +252,100 @@ 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 settingsIcon =
+ KotlinUtils.getSettingsIcon(
+ application,
+ permissionReminders.first().user,
+ applicationContext.packageManager
+ )
- 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(Constants.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())
@@ -302,15 +354,11 @@ class DrivingDecisionReminderService : Service() {
return b.build()
}
- private fun getSettingsPackageName(pm: PackageManager): String {
- val settingsIntent = Intent(Settings.ACTION_SETTINGS)
- val settingsComponent: ComponentName? = settingsIntent.resolveActivity(pm)
- return settingsComponent?.packageName ?: SETTINGS_PACKAGE_NAME_FALLBACK
- }
-
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/ecm/EnhancedConfirmationDialogActivity.kt b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt
new file mode 100644
index 000000000..e813b782a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.ecm
+
+import android.annotation.SuppressLint
+import android.app.AlertDialog
+import android.app.Dialog
+import android.app.ecm.EnhancedConfirmationManager
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.os.Process
+import android.os.UserHandle
+import android.permission.flags.Flags
+import android.text.Html
+import android.text.method.LinkMovementMethod
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.TextView
+import androidx.annotation.Keep
+import androidx.annotation.RequiresApi
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentActivity
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.Constants.EXTRA_IS_ECM_IN_APP
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.ecm.EnhancedConfirmationStatsLogUtils.DialogResult
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.role.controller.model.Roles
+
+@Keep
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+class EnhancedConfirmationDialogActivity : FragmentActivity() {
+ companion object {
+ private const val KEY_WAS_CLEAR_RESTRICTION_ALLOWED = "KEY_WAS_CLEAR_RESTRICTION_ALLOWED"
+ }
+
+ private var wasClearRestrictionAllowed: Boolean = false
+ private var dialogResult: DialogResult = DialogResult.Cancelled
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (!SdkLevel.isAtLeastV() || !Flags.enhancedConfirmationModeApisEnabled()) {
+ finish()
+ return
+ }
+ if (savedInstanceState != null) {
+ wasClearRestrictionAllowed =
+ savedInstanceState.getBoolean(KEY_WAS_CLEAR_RESTRICTION_ALLOWED)
+ return
+ }
+
+ val uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID)
+ val packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)
+ val settingIdentifier = intent.getStringExtra(Intent.EXTRA_SUBJECT)
+ val isEcmInApp = intent.getBooleanExtra(EXTRA_IS_ECM_IN_APP, false)
+
+ require(uid != Process.INVALID_UID) { "EXTRA_UID cannot be null or invalid" }
+ require(!packageName.isNullOrEmpty()) { "EXTRA_PACKAGE_NAME cannot be null or empty" }
+ require(!settingIdentifier.isNullOrEmpty()) { "EXTRA_SUBJECT cannot be null or empty" }
+
+ wasClearRestrictionAllowed =
+ setClearRestrictionAllowed(packageName, UserHandle.getUserHandleForUid(uid))
+
+ val setting = Setting.fromIdentifier(this, settingIdentifier, isEcmInApp)
+ val dialogFragment =
+ EnhancedConfirmationDialogFragment.newInstance(setting.title, setting.message)
+ dialogFragment.show(supportFragmentManager, EnhancedConfirmationDialogFragment.TAG)
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ outState.putBoolean(KEY_WAS_CLEAR_RESTRICTION_ALLOWED, wasClearRestrictionAllowed)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private fun setClearRestrictionAllowed(packageName: String, user: UserHandle): Boolean {
+ val userContext = createContextAsUser(user, 0)
+ val ecm = Utils.getSystemServiceSafe(userContext, EnhancedConfirmationManager::class.java)
+ try {
+ val wasClearRestrictionAllowed = ecm.isClearRestrictionAllowed(packageName)
+ ecm.setClearRestrictionAllowed(packageName)
+ return wasClearRestrictionAllowed
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw IllegalArgumentException("unknown package: $packageName")
+ }
+ }
+
+ private data class Setting(val title: String?, val message: CharSequence?) {
+ companion object {
+ fun fromIdentifier(
+ context: Context,
+ settingIdentifier: String,
+ isEcmInApp: Boolean
+ ): Setting {
+ val settingType = SettingType.fromIdentifier(context, settingIdentifier, isEcmInApp)
+ val label =
+ when (settingType) {
+ SettingType.PLATFORM_PERMISSION ->
+ KotlinUtils.getPermGroupLabel(
+ context,
+ PermissionMapping.getGroupOfPlatformPermission(settingIdentifier)!!
+ )
+ SettingType.PLATFORM_PERMISSION_GROUP ->
+ KotlinUtils.getPermGroupLabel(context, settingIdentifier)
+ SettingType.ROLE ->
+ context.getString(
+ Roles.get(context)[settingIdentifier]!!.shortLabelResource
+ )
+ SettingType.OTHER -> null
+ }
+ val url =
+ context.getString(R.string.help_url_action_disabled_by_restricted_settings)
+ return Setting(
+ title = settingType.titleRes?.let { context.getString(it, label) },
+ message =
+ settingType.messageRes?.let { Html.fromHtml(context.getString(it, url), 0) }
+ )
+ }
+ }
+ }
+
+ private enum class SettingType(val titleRes: Int?, val messageRes: Int?) {
+ PLATFORM_PERMISSION(
+ R.string.enhanced_confirmation_dialog_title_permission,
+ R.string.enhanced_confirmation_dialog_desc_permission
+ ),
+ PLATFORM_PERMISSION_GROUP(
+ R.string.enhanced_confirmation_dialog_title_permission,
+ R.string.enhanced_confirmation_dialog_desc_permission
+ ),
+ ROLE(
+ R.string.enhanced_confirmation_dialog_title_role,
+ R.string.enhanced_confirmation_dialog_desc_role
+ ),
+ OTHER(
+ R.string.enhanced_confirmation_dialog_title_settings_default,
+ R.string.enhanced_confirmation_dialog_desc_settings_default
+ );
+
+ companion object {
+ fun fromIdentifier(
+ context: Context,
+ settingIdentifier: String,
+ isEcmInApp: Boolean
+ ): SettingType {
+ if (!isEcmInApp) return SettingType.OTHER
+ return when {
+ PermissionMapping.isRuntimePlatformPermission(settingIdentifier) &&
+ PermissionMapping.getGroupOfPlatformPermission(settingIdentifier) != null ->
+ PLATFORM_PERMISSION
+ PermissionMapping.isPlatformPermissionGroup(settingIdentifier) ->
+ PLATFORM_PERMISSION_GROUP
+ settingIdentifier.startsWith("android.app.role.") &&
+ Roles.get(context).containsKey(settingIdentifier) -> ROLE
+ else -> SettingType.OTHER
+ }
+ }
+ }
+ }
+
+ private fun onDialogResult(dialogResult: DialogResult) {
+ this.dialogResult = dialogResult
+ setResult(
+ RESULT_OK,
+ Intent().apply { putExtra(Intent.EXTRA_RETURN_RESULT, dialogResult.statsLogValue) }
+ )
+ finish()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ if (isFinishing) {
+ EnhancedConfirmationStatsLogUtils.logDialogResultReported(
+ uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID),
+ settingIdentifier = intent.getStringExtra(Intent.EXTRA_SUBJECT)!!,
+ firstShowForApp = !wasClearRestrictionAllowed,
+ dialogResult = dialogResult
+ )
+ }
+ }
+
+ class EnhancedConfirmationDialogFragment() : DialogFragment() {
+ companion object {
+ val TAG = EnhancedConfirmationDialogFragment::class.simpleName
+ private const val KEY_TITLE = "KEY_TITLE"
+ private const val KEY_MESSAGE = "KEY_MESSAGE"
+
+ fun newInstance(title: String? = null, message: CharSequence? = null) =
+ EnhancedConfirmationDialogFragment().apply {
+ arguments =
+ Bundle().apply {
+ putString(KEY_TITLE, title)
+ putCharSequence(KEY_MESSAGE, message)
+ }
+ }
+ }
+
+ private lateinit var dialogActivity: EnhancedConfirmationDialogActivity
+
+ override fun onAttach(context: Context) {
+ super.onAttach(context)
+ dialogActivity = context as EnhancedConfirmationDialogActivity
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val title = arguments!!.getString(KEY_TITLE)
+ val message = arguments!!.getCharSequence(KEY_MESSAGE)
+
+ return AlertDialog.Builder(dialogActivity)
+ .setView(createDialogView(dialogActivity, title, message))
+ .setPositiveButton(R.string.enhanced_confirmation_dialog_ok) { _, _ ->
+ dialogActivity.onDialogResult(DialogResult.Okay)
+ }
+ .create()
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ super.onCancel(dialog)
+ dialogActivity.onDialogResult(DialogResult.Cancelled)
+ }
+
+ @SuppressLint("InflateParams")
+ private fun createDialogView(
+ context: Context,
+ title: String?,
+ message: CharSequence?
+ ): View =
+ LayoutInflater.from(context)
+ .inflate(R.layout.enhanced_confirmation_dialog, null)
+ .apply {
+ title?.let {
+ requireViewById<TextView>(R.id.enhanced_confirmation_dialog_title).text = it
+ }
+ message?.let {
+ val descTextView =
+ requireViewById<TextView>(R.id.enhanced_confirmation_dialog_desc)
+ descTextView.text = it
+ descTextView.movementMethod = LinkMovementMethod.getInstance()
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationStatsLogUtils.kt b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationStatsLogUtils.kt
new file mode 100644
index 000000000..218af9775
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationStatsLogUtils.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.ecm
+
+import android.annotation.SuppressLint
+import android.app.AppOpsManager
+import android.app.ecm.EnhancedConfirmationManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.permission.flags.Flags
+import android.util.Log
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerStatsLog
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_CANCELLED
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_LEARN_MORE
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_OK
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_SUPPRESSED
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_UNSPECIFIED
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_APPOP
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_OTHER
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_PERMISSION
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_ROLE
+import com.android.permissioncontroller.PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_UNSPECIFIED
+import com.android.permissioncontroller.permission.utils.Utils
+
+/**
+ * Provides ECM-related metrics logging for PermissionController.
+ *
+ * @hide
+ */
+object EnhancedConfirmationStatsLogUtils {
+ private val LOG_TAG = EnhancedConfirmationStatsLogUtils::class.java.simpleName
+
+ enum class DialogResult(val statsLogValue: Int) {
+ Unspecified(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_UNSPECIFIED),
+ Cancelled(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_CANCELLED),
+ LearnMore(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_LEARN_MORE),
+ Okay(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_OK),
+ Suppressed(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__RESULT__RESULT_SUPPRESSED)
+ }
+
+ enum class SettingType(val statsLogValue: Int) {
+ Unspecified(
+ ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_UNSPECIFIED
+ ),
+ Appop(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_APPOP),
+ Permission(
+ ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_PERMISSION
+ ),
+ Role(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_ROLE),
+ Other(ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED__SETTING_TYPE__SETTING_TYPE_OTHER);
+
+ companion object {
+ fun fromIdentifier(settingIdentifier: String): SettingType =
+ when {
+ settingIdentifier.startsWith("android:") -> Appop
+ settingIdentifier.startsWith("android.permission.") -> Permission
+ settingIdentifier.startsWith("android.permission-group.") -> Permission
+ settingIdentifier.startsWith("android.app.role.") -> Role
+ else -> Other
+ }
+ }
+ }
+
+ fun logDialogResultReported(
+ uid: Int,
+ settingIdentifier: String,
+ firstShowForApp: Boolean,
+ dialogResult: DialogResult
+ ) {
+ if (!SdkLevel.isAtLeastV() || !Flags.enhancedConfirmationModeApisEnabled()) {
+ return
+ }
+ val settingType = SettingType.fromIdentifier(settingIdentifier)
+
+ Log.v(
+ LOG_TAG,
+ "ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED: " +
+ "uid='$uid', " +
+ "settingIdentifier='$settingIdentifier', " +
+ "firstShowForApp='$firstShowForApp', " +
+ "settingType='$settingType', " +
+ "result='${dialogResult.statsLogValue}'"
+ )
+
+ PermissionControllerStatsLog.write(
+ PermissionControllerStatsLog.ENHANCED_CONFIRMATION_DIALOG_RESULT_REPORTED,
+ uid,
+ settingIdentifier,
+ firstShowForApp,
+ settingType.statsLogValue,
+ dialogResult.statsLogValue
+ )
+ }
+
+ @SuppressLint("MissingPermission")
+ fun isPackageEcmRestricted(context: Context, packageName: String, uid: Int): Boolean {
+ if (!SdkLevel.isAtLeastV() || !Flags.enhancedConfirmationModeApisEnabled()) {
+ return false
+ }
+ val userContext = Utils.getUserContext(context, UserHandle.getUserHandleForUid(uid))
+ val ecm = userContext.getSystemService(EnhancedConfirmationManager::class.java)
+ return try {
+ val arbitrarilyChosenRestrictedSetting = AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE
+ ecm?.isRestricted(packageName, arbitrarilyChosenRestrictedSetting) ?: false
+ } catch (e: PackageManager.NameNotFoundException) {
+ false
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
index 1f6b5272a..d23225ed3 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/HibernationPolicy.kt
@@ -42,6 +42,7 @@ import android.app.usage.UsageStatsManager.INTERVAL_DAILY
import android.app.usage.UsageStatsManager.INTERVAL_MONTHLY
import android.content.BroadcastReceiver
import android.content.ComponentName
+import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
@@ -49,6 +50,8 @@ import android.content.Intent.FLAG_RECEIVER_FOREGROUND
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.database.ContentObserver
+import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Process
@@ -59,6 +62,7 @@ import android.printservice.PrintService
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
import android.provider.Settings
+import android.provider.Settings.Secure.USER_SETUP_COMPLETE
import android.safetycenter.SafetyCenterManager
import android.safetycenter.SafetyEvent
import android.safetycenter.SafetySourceData
@@ -80,6 +84,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.preference.PreferenceManager
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.Constants
+import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.DumpableLog
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.R
@@ -99,45 +104,51 @@ 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
import com.android.permissioncontroller.permission.utils.forEachInParallel
-import java.util.Date
-import java.util.Random
-import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
+import java.util.Date
+import java.util.Random
+import java.util.concurrent.TimeUnit
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
const val PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING = "first_boot_time"
-const val PREF_KEY_BOOT_TIME_SNAPSHOT = "ah_boot_time_snapshot"
+const val PREF_KEY_SYSTEM_TIME_SNAPSHOT = "ah_boot_time_snapshot"
const val PREF_KEY_ELAPSED_REALTIME_SNAPSHOT = "ah_elapsed_realtime_snapshot"
private const val PREFS_FILE_NAME = "unused_apps_prefs"
@@ -149,8 +160,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,33 +172,37 @@ 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)
+@Suppress("MissingPermission")
fun rescanAndPushDataToSafetyCenter(
context: Context,
sessionId: Long,
@@ -193,36 +211,39 @@ 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 +252,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,60 +268,94 @@ 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() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
+ val contentResolver = context.contentResolver
if (action == Intent.ACTION_BOOT_COMPLETED || action == ACTION_SET_UP_HIBERNATION) {
+ if (isUserSetupComplete(contentResolver)) {
+ maybeInitStartTimeUnusedAppTracking(context.sharedPreferences)
+ } else {
+ // User set-up is not complete. Delay this until it is.
+ // This is best-effort as the process may be killed before set-up completes
+ // which prevents this callback from being called.
+ // In that case, unused app tracking will instead start when the job first runs
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(USER_SETUP_COMPLETE),
+ /* notifyForDescendants= */ false,
+ object : ContentObserver(/* handler= */ null) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == Settings.Secure.getUriFor(USER_SETUP_COMPLETE)
+ && isUserSetupComplete(contentResolver)) {
+ contentResolver.unregisterContentObserver(this)
+ maybeInitStartTimeUnusedAppTracking(context.sharedPreferences)
+ }
+ }
+ }
+ )
+ }
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)
+ setSystemTimeSnapshots(context.sharedPreferences)
// If this user is a profile, then its hibernation/auto-revoke will be handled by the
// 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)
+ @Suppress("MissingPermission")
+ 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"
+ )
}
}
}
if (action == Intent.ACTION_TIME_CHANGED || action == Intent.ACTION_TIMEZONE_CHANGED) {
- adjustStartTimeOfUnusedAppTracking(context.sharedPreferences)
+ adjustSnapshotTimes(context.sharedPreferences)
}
}
@@ -318,8 +375,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")
@@ -343,26 +402,34 @@ class HibernationBroadcastReceiver : BroadcastReceiver() {
* Gets apps that are unused and should hibernate as a map of the user and their hibernateable apps.
*/
@MainThread
+@Suppress("MissingPermission")
private suspend fun getAppsToHibernate(
context: Context,
): Map<UserHandle, List<LightPackageInfo>> {
val now = System.currentTimeMillis()
val startTimeOfUnusedAppTracking = getStartTimeOfUnusedAppTracking(context.sharedPreferences)
- val allPackagesByUser = AllPackageInfosLiveData.getInitializedValue(forceUpdate = true)
- val allPackagesByUserByUid = allPackagesByUser.mapValues { (_, pkgs) ->
- pkgs.groupBy { pkg -> pkg.uid }
- }
+ val allPackagesByUser =
+ AllPackageInfosLiveData.getInitializedValue(forceUpdate = true) ?: emptyMap()
+ 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()
+ ?: emptyMap()
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 +444,52 @@ private suspend fun getAppsToHibernate(
for ((user, stats) in userStats) {
var unusedUserApps = unusedApps[user] ?: continue
- unusedUserApps = unusedUserApps.filter { packageInfo ->
- val pkgName = packageInfo.packageName
+ 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)
-
- // 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 +511,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 +541,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,36 +567,36 @@ 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 */
+@Suppress("MissingPermission")
suspend fun isPackageHibernationExemptBySystem(
pkg: LightPackageInfo,
user: UserHandle,
): Boolean {
- if (!LauncherPackagesLiveData.getInitializedValue().contains(pkg.packageName)) {
+ val launcherPkgs = LauncherPackagesLiveData.getInitializedValue() ?: emptyList()
+ if (!launcherPkgs.contains(pkg.packageName)) {
if (DEBUG_HIBERNATION_POLICY) {
DumpableLog.i(LOG_TAG, "Exempted ${pkg.packageName} - Package is not on launcher")
}
return true
}
- if (!ExemptServicesLiveData[user]
- .getInitializedValue()[pkg.packageName]
- .isNullOrEmpty()) {
+ val exemptServicePkgs = ExemptServicesLiveData[user].getInitializedValue() ?: emptyMap()
+ if (!exemptServicePkgs[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 +604,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 +614,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 +632,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 +660,20 @@ 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() ?: emptyList()).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 +683,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 +696,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 +708,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 +738,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 +755,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 +768,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 +789,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_SYSTEM_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,87 +810,116 @@ 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
}
val diffSystemTime = getSystemTime(sharedPreferences).diffSystemTime
// If the value stored is older than a day adjust start time.
if (diffSystemTime > ONE_DAY_MS) {
- adjustStartTimeOfUnusedAppTracking(sharedPreferences)
+ adjustSnapshotTimes(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) {
+/**
+ * Init the start time of unused apps tracking if it has not been initialized before
+ */
+private fun maybeInitStartTimeUnusedAppTracking(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()
}
+}
+
+private fun setSystemTimeSnapshots(sharedPreferences: SharedPreferences) {
+ val systemTimeSnapshot = System.currentTimeMillis()
val realtimeSnapshot = SystemClock.elapsedRealtime()
- sharedPreferences.edit()
- .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot)
+ sharedPreferences
+ .edit()
+ .putLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, systemTimeSnapshot)
.putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, realtimeSnapshot)
.apply()
}
-private fun adjustStartTimeOfUnusedAppTracking(sharedPreferences: SharedPreferences) {
+private fun adjustSnapshotTimes(sharedPreferences: SharedPreferences) {
val systemTime = getSystemTime(sharedPreferences)
+ val editor = sharedPreferences
+ .edit()
+ .putLong(PREF_KEY_SYSTEM_TIME_SNAPSHOT, systemTime.actualSystemTime)
+ .putLong(PREF_KEY_ELAPSED_REALTIME_SNAPSHOT, systemTime.actualRealtime)
+
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
+ DumpableLog.w(LOG_TAG, "PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING is not initialized")
+ } else {
+ val adjustedStartTimeOfUnusedAppTracking =
+ startTimeOfUnusedAppTracking + systemTime.diffSystemTime
+ editor.putLong(PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ adjustedStartTimeOfUnusedAppTracking)
}
- val adjustedStartTimeOfUnusedAppTracking =
- startTimeOfUnusedAppTracking + systemTime.diffSystemTime
- 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()
+ editor.apply()
}
-/**
- * Make intent to go to unused apps page.
- */
+private fun isUserSetupComplete(contentResolver: ContentResolver): Boolean {
+ return Settings.Secure.getInt(contentResolver, USER_SETUP_COMPLETE, 0) != 0
+}
+
+/** 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,9 +927,10 @@ 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.
*/
+@Suppress("MissingPermission")
class HibernationJobService : JobService() {
var job: Job? = null
var jobStartTime: Long = -1L
@@ -812,6 +940,16 @@ class HibernationJobService : JobService() {
DumpableLog.i(LOG_TAG, "onStartJob")
}
+ if (!isUserSetupComplete(contentResolver)) {
+ if (DEBUG_HIBERNATION_POLICY) {
+ DumpableLog.i(LOG_TAG, "Skipping hibernation job because set-up is not complete")
+ }
+ jobFinished(params, /* wantsReschedule= */ true)
+ return true
+ }
+
+ maybeInitStartTimeUnusedAppTracking(sharedPreferences)
+
if (SKIP_NEXT_RUN) {
SKIP_NEXT_RUN = false
if (DEBUG_HIBERNATION_POLICY) {
@@ -822,58 +960,79 @@ 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,
+ 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,
- SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED)
- .build())
+ Process.myUserHandle()
+ )
+ 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, user: UserHandle) {
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 +1040,25 @@ 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 (DeviceUtils.isAuto(this)) {
+ val settingsIcon =
+ KotlinUtils.getSettingsIcon(application, user, applicationContext.packageManager)
+ extras.putBoolean(Constants.NOTIFICATION_EXTRA_USE_LAUNCHER_ICON, false)
+ b.setLargeIcon(settingsIcon)
+ }
+ if (
+ SdkLevel.isAtLeastT() &&
+ getSystemService(SafetyCenterManager::class.java)!!.isSafetyCenterEnabled
+ ) {
val notificationResources = KotlinUtils.getSafetyCenterNotificationResources(this)
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, notificationResources.appLabel)
@@ -902,15 +1070,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 +1098,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 +1142,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 +1164,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/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
index 16ee9b47d..69a8f74be 100644
--- a/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/hibernation/TEST_MAPPING
@@ -5,6 +5,9 @@
"options": [
{
"include-filter": "android.hibernation.cts.AppHibernationIntegrationTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
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/incident/ConfirmationActivity.java b/PermissionController/src/com/android/permissioncontroller/incident/ConfirmationActivity.java
index 0a8196524..c4a02eda4 100644
--- a/PermissionController/src/com/android/permissioncontroller/incident/ConfirmationActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/incident/ConfirmationActivity.java
@@ -16,7 +16,6 @@
package com.android.permissioncontroller.incident;
-import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -38,15 +37,20 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.activity.ComponentActivity;
+
import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.DeviceUtils;
import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.incident.wear.ConfirmationActivityWearViewHandler;
import java.util.ArrayList;
/**
* Confirmation dialog for approving an incident or bug report for sharing off the device.
*/
-public class ConfirmationActivity extends Activity implements OnClickListener, OnDismissListener {
+public class ConfirmationActivity extends ComponentActivity implements OnClickListener,
+ OnDismissListener {
private static final String TAG = "ConfirmationActivity";
/**
@@ -107,7 +111,16 @@ public class ConfirmationActivity extends Activity implements OnClickListener, O
// since we can't get proper approval. (Zero-length images or reasons means that
// we will proceed with the imageless consent dialog).
final IncidentManager incidentManager = getSystemService(IncidentManager.class);
- incidentManager.denyReport(getIntent().getData());
+ incidentManager.denyReport(uri);
+
+ if (DeviceUtils.isWear(this)) {
+ ConfirmationActivityWearViewHandler viewHandler =
+ new ConfirmationActivityWearViewHandler(this, incidentManager);
+ setContentView(viewHandler.createView());
+ viewHandler.updateViewModel(true, getString(R.string.incident_report_dialog_title),
+ getString(R.string.incident_report_error_dialog_text, appLabel), uri);
+ return;
+ }
// Show a message to the user saying... nevermind.
new AlertDialog.Builder(this)
@@ -125,6 +138,22 @@ public class ConfirmationActivity extends Activity implements OnClickListener, O
}
+ final String message = getString(R.string.incident_report_dialog_text,
+ appLabel,
+ formatting.getDate(pending.getTimestamp()),
+ formatting.getTime(pending.getTimestamp()),
+ appLabel);
+
+ if (DeviceUtils.isWear(this)) {
+ ConfirmationActivityWearViewHandler viewHandler =
+ new ConfirmationActivityWearViewHandler(this,
+ getSystemService(IncidentManager.class));
+ setContentView(viewHandler.createView());
+ viewHandler.updateViewModel(false, getString(R.string.incident_report_dialog_title),
+ message, uri);
+ return;
+ }
+
final View content = getLayoutInflater().inflate(R.layout.incident_confirmation,
null);
@@ -162,11 +191,6 @@ public class ConfirmationActivity extends Activity implements OnClickListener, O
reasonTextView.setText(spannable);
}
- final String message = getString(R.string.incident_report_dialog_text,
- appLabel,
- formatting.getDate(pending.getTimestamp()),
- formatting.getTime(pending.getTimestamp()),
- appLabel);
((TextView) content.findViewById(R.id.message)).setText(message);
final ArrayList<Drawable> images = details.getImages();
diff --git a/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java b/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java
index dead300d7..7efb89849 100644
--- a/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java
+++ b/PermissionController/src/com/android/permissioncontroller/incident/PendingList.java
@@ -40,7 +40,7 @@ import java.util.Set;
/**
* Represents the current list of pending records.
*/
-class PendingList {
+public class PendingList {
private static final String TAG = "PermissionController.incident";
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/incident/wear/ConfirmationActivityWearViewHandler.java b/PermissionController/src/com/android/permissioncontroller/incident/wear/ConfirmationActivityWearViewHandler.java
new file mode 100644
index 000000000..272a87960
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/incident/wear/ConfirmationActivityWearViewHandler.java
@@ -0,0 +1,85 @@
+/*
+ * 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.incident.wear;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.IncidentManager;
+import android.view.View;
+
+import com.android.permissioncontroller.incident.PendingList;
+
+import kotlin.Unit;
+
+/**
+ * Wear-specific view handler for the confirmation activity.
+ */
+public class ConfirmationActivityWearViewHandler {
+
+ private final Activity mActivity;
+ private final IncidentManager mIncidentManager;
+ private WearConfirmationActivityViewModel mViewModel;
+
+ public ConfirmationActivityWearViewHandler(Activity activity, IncidentManager incidentManager) {
+ mActivity = activity;
+ mIncidentManager = incidentManager;
+ }
+
+ /**
+ * Creates and returns the view hierarchy that is managed by this view
+ * handler. This must be called before {@link #updateViewModel}.
+ */
+ public View createView() {
+ WearConfirmationActivityViewModelFactory factory =
+ new WearConfirmationActivityViewModelFactory();
+ mViewModel = factory.create(
+ WearConfirmationActivityViewModel.class);
+
+ return WearConfirmationScreenKt.createView(mActivity, mViewModel);
+ }
+
+ /**
+ * Updates the wear confirmation activity view model to reflect the specified state.
+ */
+ public void updateViewModel(boolean isDenyView, String dialogTitle, String message, Uri uri) {
+ mViewModel.getShowDialogLiveData().setValue(true);
+ mViewModel.getShowDenyReportLiveData().setValue(isDenyView);
+ WearConfirmationActivityViewModel.ContentArgs args =
+ new WearConfirmationActivityViewModel.ContentArgs(
+ dialogTitle,
+ message,
+ () -> {
+ mIncidentManager.denyReport(uri);
+ mActivity.finish();
+ return Unit.INSTANCE;
+ },
+ () -> {
+ mIncidentManager.approveReport(uri);
+ PendingList.getInstance().updateState(mActivity, 0);
+ mActivity.finish();
+ return Unit.INSTANCE;
+ },
+ () -> {
+ mIncidentManager.denyReport(uri);
+ PendingList.getInstance().updateState(mActivity, 0);
+ mActivity.finish();
+ return Unit.INSTANCE;
+ }
+ );
+ mViewModel.getContentArgsLiveData().setValue(args);
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationActivityViewModel.kt b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationActivityViewModel.kt
new file mode 100644
index 000000000..aecb6222b
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationActivityViewModel.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.incident.wear
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+class WearConfirmationActivityViewModel : ViewModel() {
+ /** A livedata which stores whether the incident/bug report dialog is visible. */
+ val showDialogLiveData = MutableLiveData<Boolean>()
+
+ /** A livedata which stores to whether to show the screen for deny report */
+ val showDenyReportLiveData = MutableLiveData<Boolean>()
+
+ /** A livedata which stores arguments for a confirmation section. */
+ var contentArgsLiveData = MutableLiveData<ContentArgs>()
+
+ data class ContentArgs(
+ // stores the incident/bug report title
+ val title: String,
+ // stores the incident/bug report message body
+ val message: String,
+ // this is a button shows only in a denied incident/bug report dialog
+ val onDenyClick: () -> Unit,
+ val onOkClick: () -> Unit,
+ val onCancelClick: () -> Unit,
+ )
+
+ init {
+ showDialogLiveData.value = false
+ showDenyReportLiveData.value = false
+ contentArgsLiveData.value = null
+ }
+}
+
+/** Factory for a WearConfirmationActivityViewModel */
+class WearConfirmationActivityViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return WearConfirmationActivityViewModel() as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt
new file mode 100644
index 000000000..8e58d48d9
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.incident.wear
+
+import android.app.Activity
+import android.view.View
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.CircularProgressIndicator
+import com.android.permissioncontroller.permission.ui.wear.elements.AlertDialog
+import com.android.permissioncontroller.permission.ui.wear.elements.SingleButtonAlertDialog
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+
+@Composable
+fun WearConfirmationScreen(viewModel: WearConfirmationActivityViewModel) {
+ // Wear screen doesn't show incident/bug report's optional reasons and images.
+ val showDialog = viewModel.showDialogLiveData.observeAsState(false)
+ val showDenyReport = viewModel.showDenyReportLiveData.observeAsState(false)
+ val contentArgs = viewModel.contentArgsLiveData.observeAsState(null)
+ var isLoading by rememberSaveable { mutableStateOf(true) }
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ if (isLoading) {
+ CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
+ } else {
+ if (showDenyReport.value) {
+ contentArgs.value?.let {
+ SingleButtonAlertDialog(
+ showDialog = showDialog.value,
+ title = it.title,
+ message = it.message,
+ onButtonClick = it.onDenyClick,
+ scalingLazyListState = ScalingLazyListState(0)
+ )
+ }
+ return
+ }
+ contentArgs.value?.let {
+ AlertDialog(
+ showDialog = showDialog.value,
+ title = it.title,
+ message = it.message,
+ onOKButtonClick = it.onOkClick,
+ onCancelButtonClick = it.onCancelClick,
+ scalingLazyListState = ScalingLazyListState(0)
+ )
+ }
+ }
+ }
+
+ if (isLoading && showDialog.value) {
+ isLoading = false
+ }
+}
+
+fun createView(activity: Activity, viewModel: WearConfirmationActivityViewModel): View {
+ return ComposeView(activity).apply {
+ setContent { WearPermissionTheme { WearConfirmationScreen(viewModel) } }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
index b65eb6710..7ec419864 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/permission/TEST_MAPPING
@@ -22,7 +22,7 @@
"include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -30,7 +30,7 @@
"name": "CtsHibernationTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -58,19 +58,113 @@
"include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
{
"name": "CtsHibernationTestCases[com.google.android.permission.apex]",
"options": [
- // TODO(b/238677038): This test currently fails on R base image
{
- "exclude-filter": "android.hibernation.cts.AutoRevokeTest#testUnusedApp_uninstallApp"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.BackgroundPermissionsTest"
+ },
+ {
+ "include-filter": "android.permission.cts.LocationAccessCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.NotificationListenerCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.OneTimePermissionTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PermissionControllerTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsHibernationTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsHibernationTestCases"
+ },
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.BackgroundPermissionsTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "include-filter": "android.permission.cts.LocationAccessCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.NotificationListenerCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.OneTimePermissionTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PermissionControllerTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest"
+ }
+ ]
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsPermissionTestCases[com.google.android.permission.apex]",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.BackgroundPermissionsTest"
+ },
+ {
+ "include-filter": "android.permission.cts.LocationAccessCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.NotificationListenerCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.OneTimePermissionTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PermissionControllerTest"
+ },
+ {
+ "include-filter": "android.permission.cts.PlatformPermissionGroupMappingTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsHibernationTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677038): This test currently fails on R base image
+ {
+ "exclude-filter": "android.hibernation.cts.AutoRevokeTest#testUnusedApp_uninstallApp"
}
]
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java
deleted file mode 100644
index 637eb5fc4..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompat.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permissioncontroller.permission.compat;
-
-import android.text.Layout;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.method.LinkMovementMethod;
-import android.text.method.Touch;
-import android.view.MotionEvent;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-
-/**
- * Fixes the issue that links can be triggered for touches outside of line bounds for
- * {@link LinkMovementMethod}.
- * <p>
- * This is based on the fix in ag/22301465.
- */
-public class LinkMovementMethodCompat extends LinkMovementMethod {
- @Override
- public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer,
- @NonNull MotionEvent event) {
- int action = event.getAction();
-
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
- int x = (int) event.getX();
- int y = (int) event.getY();
-
- x -= widget.getTotalPaddingLeft();
- y -= widget.getTotalPaddingTop();
-
- x += widget.getScrollX();
- y += widget.getScrollY();
-
- Layout layout = widget.getLayout();
- boolean isOutOfLineBounds;
- if (y < 0 || y > layout.getHeight()) {
- isOutOfLineBounds = true;
- } else {
- int line = layout.getLineForVertical(y);
- isOutOfLineBounds = x < layout.getLineLeft(line) || x > layout.getLineRight(line);
- }
-
- if (isOutOfLineBounds) {
- Selection.removeSelection(buffer);
-
- // return LinkMovementMethod.super.onTouchEvent(widget, buffer, event);
- return Touch.onTouchEvent(widget, buffer, event);
- }
- }
-
- return super.onTouchEvent(widget, buffer, event);
- }
-
- /**
- * @return a {@link LinkMovementMethodCompat} instance
- */
- @NonNull
- public static LinkMovementMethodCompat getInstance() {
- if (sInstance == null) {
- sInstance = new LinkMovementMethodCompat();
- }
-
- return sInstance;
- }
-
- private static LinkMovementMethodCompat sInstance;
-}
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..c69bb54fd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/DataRepository.kt
@@ -21,7 +21,10 @@ import android.content.ComponentCallbacks2
import android.content.res.Configuration
import androidx.annotation.GuardedBy
import androidx.annotation.MainThread
+import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.utils.ContextCompat
+import com.android.permissioncontroller.permission.utils.KotlinUtils
import java.util.concurrent.TimeUnit
/**
@@ -39,18 +42,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 +61,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,17 +73,12 @@ 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
- * this is a low RAM device, eject all caches always, including upon the UI closing.
- *
- * @param level The severity of the current memory pressure
+ * Remove LiveData objects with no observer.
*/
override fun onTrimMemory(level: Int) {
if (isLowMemoryDevice) {
@@ -91,19 +86,33 @@ 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
- })
+ if (SdkLevel.isAtLeastU() && level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
+ // On UDC+ TRIM_MEMORY_BACKGROUND may be the last callback an app will receive
+ // before it's frozen.
+ trimInactiveData(TIME_THRESHOLD_ALL_NANOS)
+ return
+ }
+
+ trimInactiveData(
+ threshold =
+ when (level) {
+ ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> TIME_THRESHOLD_LAX_NANOS
+ // Allow handling for trim levels that are deprecated in newer API versions
+ // but are still supported on older devices that this code ships to.
+ @Suppress("DEPRECATION") ComponentCallbacks2.TRIM_MEMORY_MODERATE -> TIME_THRESHOLD_TIGHT_NANOS
+ @Suppress("DEPRECATION") ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> TIME_THRESHOLD_ALL_NANOS
+ @Suppress("DEPRECATION") ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> TIME_THRESHOLD_LAX_NANOS
+ @Suppress("DEPRECATION") ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> TIME_THRESHOLD_TIGHT_NANOS
+ @Suppress("DEPRECATION") ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> TIME_THRESHOLD_ALL_NANOS
+ else -> return
+ }
+ )
}
+ // Allow handling for trim levels that are deprecated in newer API versions
+ // but are still supported on older devices that this code ships to.
override fun onLowMemory() {
- onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE)
+ onTrimMemory(@Suppress("DEPRECATION") ComponentCallbacks2.TRIM_MEMORY_COMPLETE)
}
override fun onConfigurationChanged(newConfig: Configuration) {
@@ -111,9 +120,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 +134,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 +163,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.
@@ -167,7 +174,11 @@ abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper
fun invalidateAllForPackage(packageName: String) {
synchronized(lock) {
for (key in data.keys.toSet()) {
- if (key is Pair<*, *> || key is Triple<*, *, *> && key.first == packageName) {
+ if (
+ key is Pair<*, *> ||
+ key is Triple<*, *, *> ||
+ key is KotlinUtils.Quadruple<*, *, *, *> && key.first == packageName
+ ) {
data.remove(key)
}
}
@@ -176,8 +187,28 @@ abstract class DataRepositoryForPackage<K, V : DataRepository.InactiveTimekeeper
}
/**
- * A convenience to retrieve data from a repository with a composite key
+ * A DataRepository to cache LiveData for a device. The device can be a primary device with default
+ * deviceId in the key, or a remote device with virtual device Id in the key. It uses deviceId to
+ * initialize a new LiveData instance. Note: the virtual device Id should always be the last element
+ * in the composite key.
*/
+abstract class DataRepositoryForDevice<K, V : DataRepository.InactiveTimekeeper> :
+ DataRepositoryForPackage<K, V>() {
+
+ @MainThread protected abstract fun newValue(key: K, deviceId: Int): V
+
+ override fun newValue(key: K): V {
+ return newValue(key, ContextCompat.DEVICE_ID_DEFAULT)
+ }
+
+ fun getWithDeviceId(key: K, deviceId: Int): V {
+ synchronized(lock) {
+ return data.getOrPut(key) { newValue(key, deviceId) }
+ }
+ }
+}
+
+/** 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 +216,77 @@ 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 {
+ return get(Triple(k1, k2, k3))
+}
+
+/** A getter on DataRepositoryForDevice to retrieve a LiveData for a device. */
+operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepositoryForDevice<
+ Triple<K1, K2, Int>, V
+>
+ .get(k1: K1, k2: K2, deviceId: Int): V {
+ return getWithDeviceId(Triple(k1, k2, deviceId), deviceId)
+}
+
/**
- * A convenience to retrieve data from a repository with a composite key
+ * A collection of getters on DataRepositoryForDevice to conveniently retrieve a LiveData for tbe
+ * primary device. The param can be in the format of Pair<K1, K2> or [K1, K2]
*/
-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))
+operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepositoryForDevice<
+ Triple<K1, K2, Int>, V
+>
+ .get(
+ k1: K1,
+ k2: K2,
+): V {
+ return getWithDeviceId(
+ Triple(k1, k2, ContextCompat.DEVICE_ID_DEFAULT),
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
+}
+
+operator fun <K1, K2, V : DataRepository.InactiveTimekeeper> DataRepositoryForDevice<
+ Triple<K1, K2, Int>, V
+>
+ .get(key: Pair<K1, K2>): V {
+ return getWithDeviceId(
+ Triple(key.first, key.second, ContextCompat.DEVICE_ID_DEFAULT),
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
+}
+
+/** A getter on DataRepositoryForDevice to retrieve a LiveData for a device. */
+operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper> DataRepositoryForDevice<
+ KotlinUtils.Quadruple<K1, K2, K3, Int>, V
+>
+ .get(k1: K1, k2: K2, k3: K3, deviceId: Int): V {
+ return getWithDeviceId(KotlinUtils.Quadruple(k1, k2, k3, deviceId), deviceId)
+}
+
+/**
+ * A collection of getters on DataRepositoryForDevice to conveniently retrieve a LiveData for tbe
+ * primary device. The param can be in the format of Triple<K1, K2, K3> or [K1, K2, K3]
+ */
+operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper> DataRepositoryForDevice<
+ KotlinUtils.Quadruple<K1, K2, K3, Int>, V
+>
+ .get(k1: K1, k2: K2, k3: K3): V {
+ return getWithDeviceId(
+ KotlinUtils.Quadruple(k1, k2, k3, ContextCompat.DEVICE_ID_DEFAULT),
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
+}
+
+operator fun <K1, K2, K3, V : DataRepository.InactiveTimekeeper> DataRepositoryForDevice<
+ KotlinUtils.Quadruple<K1, K2, K3, Int>, V
+>
+ .get(key: Triple<K1, K2, K3>): V {
+ return getWithDeviceId(
+ KotlinUtils.Quadruple(key.first, key.second, key.third, ContextCompat.DEVICE_ID_DEFAULT),
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
}
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..67b765097 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightAppPermGroupLiveData.kt
@@ -27,6 +27,7 @@ import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
+import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.LocationUtils
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.Utils.OS_PKG
@@ -39,31 +40,35 @@ 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,
- private val user: UserHandle
+ private val user: UserHandle,
+ private val deviceId: Int
) : SmartUpdateMediatorLiveData<LightAppPermGroup?>(), LocationUtils.LocationListener {
private val LOG_TAG = this::class.java.simpleName
private var isSpecialLocation = false
- private val permStateLiveData = PermStateLiveData[packageName, permGroupName, user]
+ private val permStateLiveData = PermStateLiveData[packageName, permGroupName, user, deviceId]
private val permGroupLiveData = PermGroupLiveData[permGroupName]
- private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
+ private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user, deviceId]
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)
+ val key = KotlinUtils.Quadruple(packageName, permGroupName, user, deviceId)
addSource(permStateLiveData) { permStates ->
if (permStates == null && permStateLiveData.isInitialized) {
@@ -100,8 +105,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 +117,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 +126,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 +161,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 +214,25 @@ 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 :
+ DataRepositoryForDevice<
+ KotlinUtils.Quadruple<String, String, UserHandle, Int>, LightAppPermGroupLiveData
+ >() {
+ override fun newValue(
+ key: KotlinUtils.Quadruple<String, String, UserHandle, Int>,
+ deviceId: Int
+ ): LightAppPermGroupLiveData {
+ return LightAppPermGroupLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third,
+ deviceId
+ )
}
}
-} \ 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..ac3f670ec 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPackageInfoLiveData.kt
@@ -18,6 +18,8 @@
package com.android.permissioncontroller.permission.data
import android.app.Application
+import android.content.Context
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.UserHandle
import android.os.UserManager
@@ -27,7 +29,9 @@ import androidx.lifecycle.Observer
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
+import com.android.permissioncontroller.permission.utils.ContextCompat
import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils.isPermissionDeviceAware
import kotlinx.coroutines.Job
/**
@@ -37,11 +41,14 @@ 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),
+ private val user: UserHandle,
+ private val deviceId: Int
+) :
+ SmartAsyncMediatorLiveData<LightPackageInfo?>(alwaysUpdateOnActive = false),
PackageBroadcastReceiver.PackageBroadcastListener,
PermissionListenerMultiplexer.PermissionChangeCallback {
@@ -49,13 +56,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 +76,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 +101,51 @@ 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
+ }
+
+ val packageManager = Utils.getUserContext(app, user).packageManager
+ val pI = packageManager.getPackageInfo(packageName, flags)
- 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)
+ // PackageInfo#requestedPermissionsFlags is not device aware. Hence for device aware
+ // permissions if the deviceId is not the primary device we need to separately check
+ // permission for that device and update requestedPermissionsFlags.
+ if (SdkLevel.isAtLeastV() && deviceId != ContextCompat.DEVICE_ID_DEFAULT) {
+ val requestedPermissionsFlagsForDevice =
+ getPermissionsFlagsForDevice(
+ pI.requestedPermissions?.toList() ?: emptyList(),
+ pI.requestedPermissionsFlags?.toList() ?: emptyList(),
+ pI.applicationInfo!!.uid,
+ deviceId
+ )
+
+ LightPackageInfo(pI, deviceId, requestedPermissionsFlagsForDevice)
+ } else {
+ LightPackageInfo(pI)
+ }
+ } 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(Triple(packageName, user, deviceId))
+ 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 +158,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 +170,8 @@ class LightPackageInfoLiveData private constructor(
}
}
- private val userPackageInfosObserver = Observer<List<LightPackageInfo>> {
- updateFromUserPackageInfosLiveData()
- }
+ private val userPackageInfosObserver =
+ Observer<List<LightPackageInfo>> { updateFromUserPackageInfosLiveData() }
@MainThread
private fun updateFromUserPackageInfosLiveData() {
@@ -159,6 +188,16 @@ class LightPackageInfoLiveData private constructor(
watchingUserPackagesLiveData = false
}
+ if (SdkLevel.isAtLeastV() && deviceId != Context.DEVICE_ID_DEFAULT) {
+ packageInfo.deviceId = deviceId
+ packageInfo.requestedPermissionsFlags =
+ getPermissionsFlagsForDevice(
+ packageInfo.requestedPermissions,
+ packageInfo.requestedPermissionsFlags,
+ packageInfo.uid,
+ deviceId
+ )
+ }
value = packageInfo
} else {
// If the UserPackageInfosLiveData does not contain this package, check for removal, and
@@ -181,16 +220,57 @@ class LightPackageInfoLiveData private constructor(
}
}
+ // Given permission flags of the default device and an external device Id, return a new list of
+ // permission flags for that device by checking grant state of device aware permissions for the
+ // device.
+ private fun getPermissionsFlagsForDevice(
+ requestedPermissions: List<String>,
+ requestedPermissionsFlags: List<Int>,
+ uid: Int,
+ deviceId: Int
+ ): List<Int> {
+ val requestedPermissionsFlagsForDevice = requestedPermissionsFlags.toMutableList()
+ val deviceContext = ContextCompat.createDeviceContext(app, deviceId)
+
+ for ((idx, permName) in requestedPermissions.withIndex()) {
+ if (isPermissionDeviceAware(deviceContext, deviceId, permName)) {
+ val result = deviceContext.checkPermission(permName, -1, uid)
+
+ if (result == PackageManager.PERMISSION_GRANTED) {
+ requestedPermissionsFlagsForDevice[idx] =
+ requestedPermissionsFlagsForDevice[idx] or
+ PackageInfo.REQUESTED_PERMISSION_GRANTED
+ }
+
+ if (result == PackageManager.PERMISSION_DENIED) {
+ requestedPermissionsFlagsForDevice[idx] =
+ requestedPermissionsFlagsForDevice[idx] and
+ PackageInfo.REQUESTED_PERMISSION_GRANTED.inv()
+ }
+ }
+ }
+
+ return requestedPermissionsFlagsForDevice
+ }
+
/**
* Repository for LightPackageInfoLiveDatas
- * <p> Key value is a string package name and UserHandle pair, value is its corresponding
- * LiveData.
+ *
+ * <p> Key value is a triple of package name, UserHandle and virtual deviceId, value is its
+ * corresponding LiveData.
*/
- companion object : DataRepositoryForPackage<Pair<String, UserHandle>,
- LightPackageInfoLiveData>() {
- override fun newValue(key: Pair<String, UserHandle>): LightPackageInfoLiveData {
- return LightPackageInfoLiveData(PermissionControllerApplication.get(),
- key.first, key.second)
+ companion object :
+ DataRepositoryForDevice<Triple<String, UserHandle, Int>, LightPackageInfoLiveData>() {
+ override fun newValue(
+ key: Triple<String, UserHandle, Int>,
+ deviceId: Int
+ ): LightPackageInfoLiveData {
+ return LightPackageInfoLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ deviceId
+ )
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/LightPermInfoLiveData.kt
index 6f33cb199..091c45b92 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))
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(LOG_TAG, "Permission \"$permissionName\" not found")
- invalidateSingle(permissionName)
- null
- }
+ val newValue =
+ try {
+ LightPermInfo(app.packageManager.getPermissionInfo(permissionName, 0))
+ } 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..0c12afb02 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..02429e294 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PackageBroadcastReceiver.kt
@@ -25,35 +25,29 @@ import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData
import com.android.permissioncontroller.permission.data.v34.SafetyLabelInfoLiveData
+import com.android.permissioncontroller.permission.data.v35.PackagePermissionsExternalDeviceLiveData
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 +55,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 +78,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
+ )
}
}
}
@@ -161,6 +163,7 @@ object PackageBroadcastReceiver : BroadcastReceiver() {
LightPackageInfoLiveData.invalidateAllForPackage(packageName)
PermStateLiveData.invalidateAllForPackage(packageName)
PackagePermissionsLiveData.invalidateAllForPackage(packageName)
+ PackagePermissionsExternalDeviceLiveData.invalidateAllForPackage(packageName)
HibernationSettingStateLiveData.invalidateAllForPackage(packageName)
LightAppPermGroupLiveData.invalidateAllForPackage(packageName)
AppPermGroupUiInfoLiveData.invalidateAllForPackage(packageName)
@@ -171,9 +174,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 78f2f72c6..d44fea233 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupLiveData.kt
@@ -36,19 +36,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,25 +65,30 @@ 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
- }
-
- when (groupInfo) {
- is PermissionGroupInfo -> {
- val permInfos = try {
- Utils.getInstalledRuntimePermissionInfosForGroup(context.packageManager,
- groupName)
- } catch (e: PackageManager.NameNotFoundException) {
+ groupInfo =
+ Utils.getGroupInfo(groupName, context)
+ ?: run {
Log.e(LOG_TAG, "Invalid permission group $groupName")
invalidateSingle(groupName)
value = null
return
}
+ when (groupInfo) {
+ is PermissionGroupInfo -> {
+ val permInfos =
+ try {
+ Utils.getInstalledRuntimePermissionInfosForGroup(
+ context.packageManager,
+ groupName
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(LOG_TAG, "Invalid permission group $groupName")
+ invalidateSingle(groupName)
+ value = null
+ return
+ }
+
for (permInfo in permInfos) {
permissionInfos[permInfo.name] = LightPermInfo(permInfo)
}
@@ -105,8 +106,8 @@ class PermGroupLiveData private constructor(
value = permGroup
- val packageNames = permissionInfos.values.map { permInfo -> permInfo.packageName }
- .toMutableSet()
+ val packageNames =
+ permissionInfos.values.map { permInfo -> permInfo.packageName }.toMutableSet()
packageNames.add(groupInfo.packageName)
// TODO ntmyren: What if the package isn't installed for the system user?
@@ -123,8 +124,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()
@@ -136,6 +137,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..0ebfcd3d7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermGroupsPackagesUiInfoLiveData.kt
@@ -21,6 +21,7 @@ import android.app.role.RoleManager
import android.os.Handler
import android.os.Looper
import android.os.UserHandle
+import android.os.UserManager
import androidx.lifecycle.LiveData
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
@@ -37,8 +38,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 +57,18 @@ 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>
+ private val userManager =
+ Utils.getSystemServiceSafe(app.applicationContext, UserManager::class.java)
init {
addSource(groupNamesLiveData) {
- groupNames = it ?: emptyList()
+ groupNames = it
update()
getPermGroupPackageLiveDatas()
}
@@ -78,7 +81,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(
@@ -91,12 +94,22 @@ class PermGroupsPackagesUiInfoLiveData(
var grantedSystem = 0
var userInteractedSystem = 0
var firstGrantedSystemPackageName: String? = null
+ val showInSettingsByUsers = HashMap<UserHandle, Boolean>()
for ((packageUserPair, appPermGroup) in appPermGroups) {
if (!appPermGroup.shouldShow) {
continue
}
+ if (!showInSettingsByUsers.containsKey(packageUserPair.second)) {
+ showInSettingsByUsers[packageUserPair.second] =
+ Utils.shouldShowInSettings(packageUserPair.second, userManager)
+ }
+
+ if (showInSettingsByUsers[packageUserPair.second] == false) {
+ continue
+ }
+
if (appPermGroup.isSystem) {
if (isGranted(appPermGroup.permGrantState)) {
if (grantedSystem == 0) {
@@ -118,10 +131,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 +152,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 +197,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..358081ff5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/PermStateLiveData.kt
@@ -23,30 +23,39 @@ import android.os.UserHandle
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.model.livedatatypes.PermState
+import com.android.permissioncontroller.permission.utils.ContextCompat
+import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.Utils
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>>(),
+ private val user: UserHandle,
+ private val deviceId: Int
+) :
+ SmartAsyncMediatorLiveData<Map<String, PermState>>(),
PermissionListenerMultiplexer.PermissionChangeCallback {
- private val context = Utils.getUserContext(app, user)
- private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
+ private val context =
+ Utils.getUserContext(app, user).let {
+ if (deviceId == ContextCompat.DEVICE_ID_DEFAULT) {
+ it
+ } else ContextCompat.createDeviceContext(it, deviceId)
+ }
+ private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user, deviceId]
private val groupLiveData = PermGroupLiveData[permGroupName]
private var uid: Int? = null
@@ -58,9 +67,7 @@ class PermStateLiveData private constructor(
updateAsync()
}
- addSource(groupLiveData) {
- updateAsync()
- }
+ addSource(groupLiveData) { updateAsync() }
}
/**
@@ -75,19 +82,19 @@ class PermStateLiveData private constructor(
val packageInfo = packageInfoLiveData.value
val permissionGroup = groupLiveData.value
if (packageInfo == null || permissionGroup == null) {
- invalidateSingle(Triple(packageName, permGroupName, user))
+ invalidateSingle(KotlinUtils.Quadruple(packageName, permGroupName, user, deviceId))
postValue(null)
return
}
val permissionStates = mutableMapOf<String, PermState>()
for ((index, permissionName) in packageInfo.requestedPermissions.withIndex()) {
-
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 +112,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 +140,25 @@ 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>() {
- override fun newValue(key: Triple<String, String, UserHandle>): PermStateLiveData {
- return PermStateLiveData(PermissionControllerApplication.get(),
- key.first, key.second, key.third)
+ companion object :
+ DataRepositoryForDevice<
+ KotlinUtils.Quadruple<String, String, UserHandle, Int>, PermStateLiveData
+ >() {
+ override fun newValue(
+ key: KotlinUtils.Quadruple<String, String, UserHandle, Int>,
+ deviceId: Int
+ ): PermStateLiveData {
+ return PermStateLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second,
+ key.third,
+ deviceId
+ )
}
}
}
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..87386f69b 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
*/
@@ -40,19 +40,11 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
MediatorLiveData<T>(), DataRepository.InactiveTimekeeper {
companion object {
- const val DEBUG_UPDATES = false
+ const val DEBUG = false
val LOG_TAG = SmartUpdateMediatorLiveData::class.java.simpleName
}
/**
- * 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) {
@@ -95,7 +86,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
*/
@MainThread
fun update() {
- if (DEBUG_UPDATES) {
+ if (DEBUG) {
Log.i(LOG_TAG, "update ${javaClass.simpleName} ${shortStackTrace()}")
}
@@ -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,15 +113,20 @@ 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)
}
private fun <S : Any?> addSourceWithStackTraceAttribution(
source: LiveData<S>,
- onChanged: Observer<in S>,
- stackTrace: Array<StackTraceElement>
+ onChanged: Observer<in S>
) {
+ val stackTrace =
+ if (DEBUG) {
+ IllegalStateException().stackTrace
+ } else {
+ null
+ }
+
GlobalScope.launch(Main.immediate) {
if (source is SmartUpdateMediatorLiveData) {
if (source in sources) {
@@ -143,7 +137,9 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
try {
super.addSource(source, onChanged)
} catch (ex: IllegalStateException) {
- ex.setStackTrace(stackTrace)
+ if (DEBUG) {
+ ex.stackTrace = stackTrace!!
+ }
throw ex
}
}
@@ -167,8 +163,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 +171,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) {
@@ -185,8 +180,6 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
val removed = toRemove.map { have.remove(it) }.toMutableList()
- val stackTrace = IllegalStateException().getStackTrace()
-
GlobalScope.launch(Main.immediate) {
// If any state got out of sorts before this coroutine ran, correct it
for (key in toRemove) {
@@ -201,14 +194,15 @@ 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)
+ addSourceWithStackTraceAttribution(liveData, observer)
}
}
return toAdd to toRemove
@@ -218,8 +212,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()
@@ -239,7 +236,7 @@ abstract class SmartUpdateMediatorLiveData<T>(private val isStaticVal: Boolean =
* @param staleOk whether [isStale] value is ok to return
* @param forceUpdate whether to call [update] (usually triggers an IPC)
*/
- suspend fun getInitializedValue(staleOk: Boolean = false, forceUpdate: Boolean = false): T {
+ suspend fun getInitializedValue(staleOk: Boolean = false, forceUpdate: Boolean = false): T? {
return getInitializedValue(
observe = { observer ->
observeForever(observer)
@@ -247,6 +244,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/repository/v31/PermissionRepository.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/repository/v31/PermissionRepository.kt
new file mode 100644
index 000000000..4a6cb36e5
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/repository/v31/PermissionRepository.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.data.repository.v31
+
+import android.app.Application
+import android.content.Context
+import android.content.pm.PackageItemInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.text.TextUtils
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import kotlin.concurrent.Volatile
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+ * This repository encapsulates permission data (i.e. permission or permission group definitions,
+ * package permission states etc.) exposed by [android.permission.PermissionManager] and
+ * [PackageManager].
+ */
+interface PermissionRepository {
+ /**
+ * Gets the flags associated with a permission.
+ *
+ * @see PackageManager.getPermissionFlags
+ */
+ suspend fun getPermissionFlags(
+ permissionName: String,
+ packageName: String,
+ user: UserHandle
+ ): Int
+
+ /**
+ * Gets a permission group label for a given permission group.
+ *
+ * @see PackageManager.getPermissionGroupInfo
+ * @see PackageItemInfo.loadSafeLabel
+ */
+ suspend fun getPermissionGroupLabel(context: Context, groupName: String): CharSequence
+
+ /** Gets a list of permission group to be shown in the privacy dashboard. */
+ fun getPermissionGroupsForPrivacyDashboard(): List<String>
+
+ companion object {
+ @Volatile private var instance: PermissionRepository? = null
+
+ fun getInstance(application: Application): PermissionRepository =
+ instance
+ ?: synchronized(this) {
+ PermissionRepositoryImpl(application).also { instance = it }
+ }
+ }
+}
+
+class PermissionRepositoryImpl(
+ application: Application,
+ private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
+) : PermissionRepository {
+ private val packageManager = application.packageManager
+
+ override suspend fun getPermissionFlags(
+ permissionName: String,
+ packageName: String,
+ user: UserHandle
+ ): Int =
+ withContext(dispatcher) {
+ packageManager.getPermissionFlags(permissionName, packageName, user)
+ }
+
+ /**
+ * Gets a permission group's label from the system.
+ *
+ * @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
+ */
+ override suspend fun getPermissionGroupLabel(
+ context: Context,
+ groupName: String
+ ): CharSequence =
+ withContext(dispatcher) {
+ val groupInfo = getPermissionGroupInfo(groupName, context)
+ groupInfo?.loadSafeLabel(
+ context.packageManager,
+ 0f,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE or TextUtils.SAFE_STRING_FLAG_TRIM
+ )
+ ?: groupName
+ }
+
+ /**
+ * Get the [PackageItemInfo] for the given permission group.
+ *
+ * @param groupName the group
+ * @param context the `Context` to retrieve `PackageManager`
+ * @return The info of permission group or null if the group does not have runtime permissions.
+ */
+ private fun getPermissionGroupInfo(groupName: String, context: Context): PackageItemInfo? {
+ return try {
+ context.packageManager.getPermissionGroupInfo(groupName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+ ?: try {
+ context.packageManager.getPermissionInfo(groupName, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+ }
+
+ override fun getPermissionGroupsForPrivacyDashboard(): List<String> {
+ return if (SdkLevel.isAtLeastT()) {
+ PermissionMapping.getPlatformPermissionGroups().filter {
+ it != android.Manifest.permission_group.NOTIFICATIONS
+ }
+ } else {
+ PermissionMapping.getPlatformPermissionGroups()
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
deleted file mode 100644
index 6483c9f9c..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v31/AllLightPackageOpsLiveData.kt
+++ /dev/null
@@ -1,149 +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.permissioncontroller.permission.data.v31
-
-import android.app.AppOpsManager
-import android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA
-import android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE
-import android.app.AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO
-import android.app.Application
-import android.os.UserHandle
-import android.os.UserManager
-import com.android.modules.utils.build.SdkLevel
-import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
-import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData
-import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightPackageOps
-import com.android.permissioncontroller.permission.utils.PermissionMapping
-import kotlinx.coroutines.Job
-
-/**
- * LiveData class tracking [LightPackageOps] for all packages on the device and for all system
- * permission groups' ops.
- *
- * App ops data is retrieved from [AppOpsManager] and is updated whenever app ops data changes are
- * heard.
- */
-class AllLightPackageOpsLiveData(app: Application) :
- SmartAsyncMediatorLiveData<Map<Pair<String, UserHandle>, LightPackageOps>>(),
- AppOpsManager.OnOpActiveChangedListener,
- AppOpsManager.OnOpNotedListener,
- AppOpsManager.OnOpChangedListener {
-
- private val appOpsManager = app.getSystemService(AppOpsManager::class.java)!!
- private val userManager = app.getSystemService(UserManager::class.java)!!
- private var opNames: MutableSet<String> =
- getOpNames(StandardPermGroupNamesLiveData.value).toMutableSet()
-
- init {
- addSource(StandardPermGroupNamesLiveData) {
- opNames = getOpNames(it).toMutableSet()
- opNames.add(OPSTR_PHONE_CALL_MICROPHONE)
- opNames.add(OPSTR_PHONE_CALL_CAMERA)
- if (SdkLevel.isAtLeastT()) {
- opNames.add(OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO)
- }
-
- update()
- }
- }
-
- override fun onActive() {
- super.onActive()
-
- opNames.forEach { opName ->
- // TODO(b/262035952): We watch each active op individually as startWatchingActive only
- // registers the callback if all ops are valid. Fix this behavior so if one op is
- // invalid it doesn't affect the other ops.
- try {
- appOpsManager.startWatchingActive(arrayOf(opName), { it.run() }, this)
- } catch (ignored: IllegalArgumentException) {
- // Older builds may not support all requested app ops.
- }
-
- try {
- appOpsManager.startWatchingMode(opName, /* all packages */ null, this)
- } catch (ignored: IllegalArgumentException) {
- // Older builds may not support all requested app ops.
- }
-
- if (SdkLevel.isAtLeastU()) {
- try {
- appOpsManager.startWatchingNoted(arrayOf(opName), this)
- } catch (ignored: IllegalArgumentException) {
- // Older builds may not support all requested app ops.
- }
- }
- }
- }
-
- override fun onInactive() {
- super.onInactive()
-
- appOpsManager.stopWatchingActive(this)
- appOpsManager.stopWatchingMode(this)
- }
-
- override suspend fun loadDataAndPostValue(job: Job) {
- if (opNames.isEmpty()) {
- return
- }
-
- val packageOpsList =
- try {
- appOpsManager.getPackagesForOps(opNames.toTypedArray())
- } catch (e: NullPointerException) {
- // Older builds may not support all requested app ops.
- emptyList<AppOpsManager.PackageOps>()
- }
-
- val allProfilesInCurrentUser = userManager.userProfiles
-
- postValue(
- packageOpsList
- .filter { UserHandle.getUserHandleForUid(it.uid) in allProfilesInCurrentUser }
- .associateBy(
- { Pair(it.packageName, UserHandle.getUserHandleForUid(it.uid)) },
- { LightPackageOps(opNames, it) }))
- }
-
- override fun onOpChanged(op: String?, packageName: String?) {
- update()
- }
-
- override fun onOpActiveChanged(op: String, uid: Int, packageName: String, active: Boolean) {
- update()
- }
-
- override fun onOpNoted(
- code: String,
- uid: Int,
- packageName: String,
- attributionTag: String?,
- flags: Int,
- result: Int
- ) {
- update()
- }
-
- /** Returns all op names for all permissions in a list of permission groups. */
- private fun getOpNames(permissionGroupNames: List<String>?) =
- permissionGroupNames
- ?.flatMap { group -> PermissionMapping.getPlatformPermissionNamesOfGroup(group) }
- ?.mapNotNull { permName -> AppOpsManager.permissionToOp(permName) }
- ?.toSet()
- ?: setOf()
-}
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..b033068ab 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v34/SafetyLabelInfoLiveData.kt
@@ -17,6 +17,7 @@
package com.android.permissioncontroller.permission.data.v34
import android.app.Application
+import android.content.Context
import android.content.pm.PackageManager
import android.os.Process
import android.os.UserHandle
@@ -28,6 +29,7 @@ import com.android.permissioncontroller.permission.data.PackageBroadcastReceiver
import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.model.livedatatypes.v34.SafetyLabelInfo
+import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils
import kotlinx.coroutines.Job
/**
@@ -88,9 +90,22 @@ private constructor(
return
}
+ val userContext =
+ if (user == Process.myUserHandle()) {
+ app
+ } else {
+ app.createContextAsUser(user, /* flags= */ 0)
+ }
+
+ // Asl in Apk (V+) is not supported by permissions
+ if (!SafetyLabelUtils.isAppMetadataSourceSupported(userContext, packageName)) {
+ postValue(SafetyLabelInfo.UNAVAILABLE)
+ return
+ }
+
val safetyLabelInfo: SafetyLabelInfo =
try {
- val safetyLabel: SafetyLabel? = getSafetyLabel(packageName, user)
+ val safetyLabel: SafetyLabel? = getSafetyLabel(userContext, packageName)
if (safetyLabel != null) {
SafetyLabelInfo(safetyLabel, lightInstallSourceInfo)
} else {
@@ -106,16 +121,10 @@ private constructor(
/** Returns the [SafetyLabel] for the given package and user. */
@Throws(PackageManager.NameNotFoundException::class)
- private fun getSafetyLabel(packageName: String, user: UserHandle): SafetyLabel? {
- val userContext =
- if (user == Process.myUserHandle()) {
- app
- } else {
- app.createContextAsUser(user, /* flags= */ 0)
- }
-
+ private fun getSafetyLabel(userContext: Context, packageName: String): SafetyLabel? {
return SafetyLabel.getSafetyLabelFromMetadata(
- userContext.packageManager.getAppMetadata(packageName))
+ userContext.packageManager.getAppMetadata(packageName)
+ )
}
companion object :
@@ -124,7 +133,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/data/v35/PackagePermissionsExternalDeviceLiveData.kt b/PermissionController/src/com/android/permissioncontroller/permission/data/v35/PackagePermissionsExternalDeviceLiveData.kt
new file mode 100644
index 000000000..a85879ac4
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/data/v35/PackagePermissionsExternalDeviceLiveData.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.data.v35
+
+import android.app.Application
+import android.companion.virtual.VirtualDeviceManager
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.UserHandle
+import android.permission.PermissionManager
+import android.permission.PermissionManager.PermissionState
+import androidx.annotation.RequiresApi
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.data.DataRepositoryForPackage
+import com.android.permissioncontroller.permission.data.SmartAsyncMediatorLiveData
+import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import kotlinx.coroutines.Job
+
+/**
+ * LiveData that loads all the external device permissions per package. The permissions will be
+ * loaded only if the package has requested the permission. This live data produces the list of
+ * {@link ExternalDeviceGrantInfo} that has group name to which permission belongs to, grant state
+ * and persistentDeviceId
+ *
+ * @param app The current Application
+ * @param packageName The name of the package
+ * @param user The user for whom the packageInfo will be defined
+ */
+class PackagePermissionsExternalDeviceLiveData
+private constructor(private val app: Application, val packageName: String, val user: UserHandle) :
+ SmartAsyncMediatorLiveData<
+ List<PackagePermissionsExternalDeviceLiveData.ExternalDeviceGrantInfo>
+ >() {
+ private val permissionManager = app.getSystemService(PermissionManager::class.java)!!
+
+ data class ExternalDeviceGrantInfo(
+ val groupName: String,
+ val permGrantState: AppPermGroupUiInfo.PermGrantState,
+ val persistentDeviceId: String
+ )
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ override suspend fun loadDataAndPostValue(job: Job) {
+ if (!SdkLevel.isAtLeastV()) {
+ return
+ }
+ val virtualDeviceManager = app.getSystemService(VirtualDeviceManager::class.java) ?: return
+ val externalDeviceGrantInfoList =
+ virtualDeviceManager.allPersistentDeviceIds
+ .map { getVirtualDeviceGrantInfoList(it) }
+ .toList()
+ .flatten()
+ postValue(externalDeviceGrantInfoList)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private fun getVirtualDeviceGrantInfoList(
+ persistentDeviceId: String
+ ): List<ExternalDeviceGrantInfo> {
+ val permissionState =
+ permissionManager.getAllPermissionStates(packageName, persistentDeviceId)
+ return permissionState.mapNotNull { (permissionName, permissionState) ->
+ PermissionMapping.getGroupOfPlatformPermission(permissionName)?.let { groupName ->
+ val grantState = getGrantState(permissionState)
+ ExternalDeviceGrantInfo(groupName, grantState, persistentDeviceId)
+ }
+ }
+ }
+
+ /**
+ * This method returns the GrantState for currently supported virtual device permissions
+ * (Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO).
+ *
+ * TODO: b/328841671 (Unite this with PermGroupUiInfoLiveData#getGrantedIncludingBackground)
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private fun getGrantState(permissionState: PermissionState): AppPermGroupUiInfo.PermGrantState =
+ if (permissionState.isGranted) {
+ AppPermGroupUiInfo.PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY
+ } else if (permissionState.flags and PackageManager.FLAG_PERMISSION_ONE_TIME != 0) {
+ AppPermGroupUiInfo.PermGrantState.PERMS_ASK
+ } else {
+ AppPermGroupUiInfo.PermGrantState.PERMS_DENIED
+ }
+
+ companion object :
+ DataRepositoryForPackage<
+ Pair<String, UserHandle>, PackagePermissionsExternalDeviceLiveData
+ >() {
+ override fun newValue(
+ key: Pair<String, UserHandle>
+ ): PackagePermissionsExternalDeviceLiveData {
+ return PackagePermissionsExternalDeviceLiveData(
+ PermissionControllerApplication.get(),
+ key.first,
+ key.second
+ )
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PackagePermissionGroupUsageModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PackagePermissionGroupUsageModel.kt
new file mode 100644
index 000000000..85100ce2c
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PackagePermissionGroupUsageModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.domain.model.v31
+
+/** This data class stores all data accesses (derived from app ops) for a package and user. */
+data class PackagePermissionGroupUsageModel(
+ val packageName: String,
+ /** Permission group and recent usage time in milliseconds since the epoch */
+ val usages: Map<String, Long>,
+ val userId: Int
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt
new file mode 100644
index 000000000..4b2364cc5
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/model/v31/PermissionGroupUsageModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.domain.model.v31
+
+/** Represents the private data access protected by the permission group. */
+data class PermissionGroupUsageModel(
+ val permissionGroup: String,
+ /** Milliseconds since the epoch */
+ val lastAccessTimestampMillis: Long,
+ /**
+ * Represents whether the permission group is highly visible to the user. Permission groups for
+ * non system apps are always considered user sensitive and the usages are always shown in the
+ * dashboard. If the permission group is not user sensitive (in case of system apps), those
+ * usages are only shown when user click "Show system" button.
+ */
+ val isUserSensitive: Boolean,
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt
new file mode 100644
index 000000000..7e079949a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/domain/usecase/v31/GetPermissionGroupUsageUseCase.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.domain.usecase.v31
+
+import android.Manifest
+import android.app.Application
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.util.Log
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel
+import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository
+import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
+import com.android.permissioncontroller.permission.domain.model.v31.PackagePermissionGroupUsageModel
+import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository
+import com.android.permissioncontroller.role.data.repository.v31.RoleRepository
+import com.android.permissioncontroller.user.data.repository.v31.UserRepository
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * This use case read app ops data and transform that data to show the private data access by apps
+ * in privacy dashboard.
+ */
+class GetPermissionGroupUsageUseCase(
+ private val packageRepository: PackageRepository,
+ private val permissionRepository: PermissionRepository,
+ private val appOpRepository: AppOpRepository,
+ private val roleRepository: RoleRepository,
+ private val userRepository: UserRepository,
+) {
+ /**
+ * Returns a flow (i.e. a stream) of permission group usages (i.e. the private data accesses)
+ * for privacy dashboard page.
+ */
+ operator fun invoke(): Flow<List<PermissionGroupUsageModel>> {
+ return appOpRepository.packageAppOpsUsages.map { packagesOps ->
+ val exemptedPackages = roleRepository.getExemptedPackages()
+ val currentUsers = userRepository.getUserProfilesIncludingCurrentUser()
+
+ packagesOps
+ .mapToPermissionGroups()
+ .filter { it.userId in currentUsers }
+ .filter { it.packageName !in exemptedPackages }
+ .filterQuietProfilesIfNeeded(currentUsers)
+ .filterNonRequestedOps()
+ .buildPermissionGroupUsageModels()
+ }
+ }
+
+ /** filter private space usages if needed. */
+ private suspend fun List<PackagePermissionGroupUsageModel>.filterQuietProfilesIfNeeded(
+ currentUsers: List<Int>
+ ): List<PackagePermissionGroupUsageModel> {
+ if (!SdkLevel.isAtLeastV()) {
+ return this
+ }
+ val usersQuietModeEnabledMap =
+ currentUsers.associateWith { userId -> userRepository.isQuietModeEnabled(userId) }
+ val usersShouldShowInQuietModeMap =
+ currentUsers.associateWith { userId -> userRepository.shouldShowInQuietMode(userId) }
+ return filter {
+ val isQuietModeEnabled = checkNotNull(usersQuietModeEnabledMap[it.userId])
+ val shouldShowInQuietMode = checkNotNull(usersShouldShowInQuietModeMap[it.userId])
+ !isQuietModeEnabled || shouldShowInQuietMode
+ }
+ }
+
+ private fun List<PackageAppOpUsageModel>.mapToPermissionGroups():
+ List<PackagePermissionGroupUsageModel> {
+ return mapNotNull { packageOps ->
+ val permissionGroupUsages =
+ packageOps.usages
+ .mapNotNull {
+ val permissionGroup =
+ PermissionMapping.getPlatformPermissionGroupForOp(it.appOpName)
+ if (permissionGroup != null) {
+ Pair(permissionGroup, it.lastAccessTimestampMillis)
+ } else {
+ Log.w(LOG_TAG, "No permission group found for op: ${it.appOpName}")
+ null
+ }
+ }
+ .groupBy { it.first } // group by permission group name
+ .map { it -> // keep permission group and recent usage time
+ it.key to it.value.map { it.second }.maxOf { it }
+ }
+ .toMap()
+
+ if (permissionGroupUsages.isNotEmpty()) {
+ PackagePermissionGroupUsageModel(
+ packageOps.packageName,
+ permissionGroupUsages,
+ packageOps.userId
+ )
+ } else {
+ null
+ }
+ }
+ }
+
+ /** Filter Ops where the corresponding permission group is no longer requested by the package */
+ private suspend fun List<PackagePermissionGroupUsageModel>.filterNonRequestedOps():
+ List<PackagePermissionGroupUsageModel> {
+ return mapNotNull { pkgOps ->
+ val userHandle = UserHandle.of(pkgOps.userId)
+ val packageInfo = packageRepository.getPackageInfo(pkgOps.packageName, userHandle)
+ val filteredOps =
+ pkgOps.usages.filter { permissionGroupUsage ->
+ packageInfo?.requestedPermissions?.any { permission ->
+ permissionGroupUsage.key ==
+ PermissionMapping.getGroupOfPlatformPermission(permission)
+ }
+ ?: false
+ }
+ if (filteredOps.isNotEmpty()) {
+ PackagePermissionGroupUsageModel(pkgOps.packageName, filteredOps, pkgOps.userId)
+ } else {
+ null
+ }
+ }
+ }
+
+ private suspend fun List<PackagePermissionGroupUsageModel>.buildPermissionGroupUsageModels():
+ List<PermissionGroupUsageModel> {
+ return flatMap { pkgOps ->
+ pkgOps.usages.map { permGroupLastAccessTimeEntry ->
+ PermissionGroupUsageModel(
+ permGroupLastAccessTimeEntry.key,
+ permGroupLastAccessTimeEntry.value,
+ isPermissionGroupUserSensitive(
+ pkgOps.packageName,
+ permGroupLastAccessTimeEntry.key,
+ pkgOps.userId
+ )
+ )
+ }
+ }
+ }
+
+ /**
+ * Determines if an app's permission group is user-sensitive. if the permission group is not
+ * user sensitive then its only shown when user choose `Show system` option
+ */
+ private suspend fun isPermissionGroupUserSensitive(
+ packageName: String,
+ permissionGroup: String,
+ userId: Int
+ ): Boolean {
+ if (isTelecomPackage(packageName, permissionGroup)) {
+ return false
+ }
+ val userHandle = UserHandle.of(userId)
+ val packageInfo = packageRepository.getPackageInfo(packageName, userHandle) ?: return false
+ // if not a system app, the permission group must be user sensitive
+ if (packageInfo.applicationFlags and ApplicationInfo.FLAG_SYSTEM == 0) {
+ return true
+ }
+
+ packageInfo.requestedPermissions.forEachIndexed { index, permissionName ->
+ if (PermissionMapping.getGroupOfPlatformPermission(permissionName) == permissionGroup) {
+ val permFlags =
+ permissionRepository.getPermissionFlags(permissionName, packageName, userHandle)
+ val packageFlags = packageInfo.requestedPermissionsFlags[index]
+ val isPermissionGranted =
+ packageFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED != 0 &&
+ permFlags and PackageManager.FLAG_PERMISSION_REVOKED_COMPAT == 0
+ if (isPermissionUserSensitive(isPermissionGranted, permFlags)) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ private fun isTelecomPackage(packageName: String, permissionGroup: String): Boolean {
+ return packageName == TELECOM_PACKAGE &&
+ (permissionGroup == Manifest.permission_group.CAMERA ||
+ permissionGroup == Manifest.permission_group.MICROPHONE)
+ }
+
+ private fun isPermissionUserSensitive(
+ isPermissionGranted: Boolean,
+ permissionFlags: Int
+ ): Boolean {
+ return if (isPermissionGranted) {
+ permissionFlags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED != 0
+ } else {
+ permissionFlags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED != 0
+ }
+ }
+
+ companion object {
+ private val LOG_TAG = GetPermissionGroupUsageUseCase::class.java.simpleName
+ private const val TELECOM_PACKAGE = "com.android.server.telecom"
+
+ fun create(app: Application): GetPermissionGroupUsageUseCase {
+ val permissionRepository = PermissionRepository.getInstance(app)
+ val userRepository = UserRepository.getInstance(app)
+ val packageRepository = PackageRepository.getInstance(app)
+ val roleRepository = RoleRepository.getInstance(app)
+ val appOpRepository = AppOpRepository.getInstance(app, permissionRepository)
+
+ return GetPermissionGroupUsageUseCase(
+ packageRepository,
+ permissionRepository,
+ appOpRepository,
+ roleRepository,
+ userRepository
+ )
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java
index e096a1a7e..3b2cc7ee0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/AppPermissionGroup.java
@@ -56,6 +56,7 @@ import com.android.permissioncontroller.PermissionControllerApplication;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.service.LocationAccessCheck;
import com.android.permissioncontroller.permission.utils.ArrayUtils;
+import com.android.permissioncontroller.permission.utils.ContextCompat;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.LocationUtils;
import com.android.permissioncontroller.permission.utils.PermissionMapping;
@@ -336,8 +337,14 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
continue;
}
- final boolean granted = (packageInfo.requestedPermissionsFlags[i]
- & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+ boolean granted;
+ if (ContextCompat.getDeviceId(context) == ContextCompat.DEVICE_ID_DEFAULT) {
+ granted = (packageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+ } else {
+ int result = packageManager.checkPermission(requestedPermission, packageName);
+ granted = result == PackageManager.PERMISSION_GRANTED;
+ }
final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
|| (isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals(
@@ -936,8 +943,12 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
}
boolean wasGranted = permission.isGrantedIncludingAppOp();
+ boolean isPermissionSplitFromNonRuntime = KotlinUtils.isPermissionSplitFromNonRuntime(
+ mContext,
+ permission.getName(),
+ mPackageInfo.applicationInfo.targetSdkVersion);
- if (mAppSupportsRuntimePermissions) {
+ if (mAppSupportsRuntimePermissions && !isPermissionSplitFromNonRuntime) {
// Do not touch permissions fixed by the system.
if (permission.isSystemFixed()) {
wasAllGranted = false;
@@ -1127,7 +1138,14 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
boolean wasGranted = permission.isGrantedIncludingAppOp();
- if (mAppSupportsRuntimePermissions) {
+ boolean isPermissionSplitFromNonRuntime =
+ KotlinUtils.isPermissionSplitFromNonRuntime(
+ mContext,
+ permission.getName(),
+ mPackageInfo.applicationInfo.targetSdkVersion);
+
+ if (mAppSupportsRuntimePermissions && !isPermissionSplitFromNonRuntime) {
+
// Revoke the permission if needed.
if (permission.isGranted()) {
permission.setGranted(false);
@@ -1172,6 +1190,8 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
if (!permission.isRevokedCompat()) {
permission.setRevokedCompat(true);
}
+
+ permission.setRevokeWhenRequested(false);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/model/Permission.java b/PermissionController/src/com/android/permissioncontroller/permission/model/Permission.java
index 5ddea4605..4daaeaec8 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/Permission.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/Permission.java
@@ -169,6 +169,18 @@ public final class Permission {
}
/**
+ * Sets the REVOKE_WHEN_REQUESTED permission flag
+ * @param revokeWhenRequested true to set the flag, false to unset it
+ */
+ public void setRevokeWhenRequested(boolean revokeWhenRequested) {
+ if (revokeWhenRequested) {
+ mFlags |= PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ } else {
+ mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ }
+ }
+
+ /**
* Sets the one-time permission flag
* @param oneTime true to set the flag, false to unset it
*/
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 3c87f0b7a..a5736ca83 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightAppPermGroup.kt
@@ -28,12 +28,12 @@ import android.os.UserHandle
* @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,
@@ -48,61 +48,58 @@ 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)
+ /** The device ID of this group, inferred from LightPackageInfo */
+ val deviceId = packageInfo.deviceId
+
/**
* The names of all background permissions in the permission group which are requested by the
* package.
*/
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
- }
-
- val foreground = AppPermSubGroup(permissions.filter { it.key in foregroundPermNames },
- packageInfo, specialLocationGrant)
-
- val background = AppPermSubGroup(permissions.filter { it.key in backgroundPermNames },
- packageInfo, specialLocationGrant)
-
- /**
- * Whether or not this App Permission Group has a permission which has a background mode
- */
+ /** 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 foreground =
+ AppPermSubGroup(
+ permissions.filter { it.key in foregroundPermNames },
+ packageInfo,
+ specialLocationGrant
+ )
+
+ val background =
+ AppPermSubGroup(
+ permissions.filter { it.key in backgroundPermNames },
+ packageInfo,
+ specialLocationGrant
+ )
+
+ /** 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
@@ -114,107 +111,91 @@ 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.isGrantedIncludingAppOp }) ||
(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 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 specialLocationGrant: Boolean?
) {
- /**
- * Whether any of this App Permission SubGroup's permissions are granted
- */
+ /** Whether any of this App Permission SubGroup's permissions are granted */
val isGranted = specialLocationGrant ?: permissions.any { it.value.isGrantedIncludingAppOp }
/**
* 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.)
- */
- val isGrantedExcludingRWROrAllRWR = specialLocationGrant ?: (permissions
- .any { it.value.isGrantedIncludingAppOp && !it.value.isRevokeWhenRequested } ||
- permissions.all { it.value.isGrantedIncludingAppOp && it.value.isRevokeWhenRequested })
-
- /**
- * Whether any of this App Permission SubGroup's permissions are granted by default
+ * RevokeWhenRequested.)
*/
+ val isGrantedExcludingRWROrAllRWR =
+ specialLocationGrant
+ ?: (permissions.any {
+ it.value.isGrantedIncludingAppOp && !it.value.isRevokeWhenRequested
+ } ||
+ permissions.all {
+ it.value.isGrantedIncludingAppOp && it.value.isRevokeWhenRequested
+ })
+
+ /** 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.isGrantedIncludingAppOp && !it.value.isOneTime }
/**
@@ -222,24 +203,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 }
/**
@@ -251,10 +224,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 0f6b6c000..ab8afae08 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPackageInfo.kt
@@ -23,7 +23,9 @@ import android.content.pm.Attribution
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.UserHandle
+import android.util.Log
import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.permission.utils.ContextCompat
import com.android.permissioncontroller.permission.utils.Utils
/**
@@ -43,7 +45,7 @@ data class LightPackageInfo(
val packageName: String,
val permissions: List<LightPermInfo>,
val requestedPermissions: List<String>,
- val requestedPermissionsFlags: List<Int>,
+ var requestedPermissionsFlags: List<Int>,
val uid: Int,
val targetSdkVersion: Int,
val isInstantApp: Boolean,
@@ -52,7 +54,8 @@ data class LightPackageInfo(
val firstInstallTime: Long,
val lastUpdateTime: Long,
val areAttributionsUserVisible: Boolean,
- val attributionTagsToLabels: Map<String, Int>
+ val attributionTagsToLabels: Map<String, Int>,
+ var deviceId: Int
) {
constructor(
pI: PackageInfo
@@ -61,23 +64,35 @@ data class LightPackageInfo(
pI.permissions?.map { perm -> LightPermInfo(perm) } ?: emptyList(),
pI.requestedPermissions?.toList() ?: emptyList(),
pI.requestedPermissionsFlags?.toList() ?: emptyList(),
- pI.applicationInfo.uid,
- pI.applicationInfo.targetSdkVersion,
- pI.applicationInfo.isInstantApp,
- pI.applicationInfo.enabled,
- pI.applicationInfo.flags,
+ pI.applicationInfo!!.uid,
+ pI.applicationInfo!!.targetSdkVersion,
+ pI.applicationInfo!!.isInstantApp,
+ pI.applicationInfo!!.enabled,
+ pI.applicationInfo!!.flags,
pI.firstInstallTime,
pI.lastUpdateTime,
- if (SdkLevel.isAtLeastS()) pI.applicationInfo.areAttributionsUserVisible() else false,
- if (SdkLevel.isAtLeastS()) buildAttributionTagsToLabelsMap(pI.attributions) else emptyMap())
+ if (SdkLevel.isAtLeastS()) pI.applicationInfo!!.areAttributionsUserVisible() else false,
+ if (SdkLevel.isAtLeastS()) buildAttributionTagsToLabelsMap(pI.attributions) else emptyMap(),
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
+
+ constructor(
+ pI: PackageInfo,
+ deviceId: Int,
+ requestedPermissionsFlagsForDevice: List<Int>
+ ) : this(pI) {
+ this.deviceId = deviceId
+ this.requestedPermissionsFlags = requestedPermissionsFlagsForDevice
+ }
/** 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])
}
}
@@ -89,9 +104,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 {
@@ -112,8 +126,16 @@ data class LightPackageInfo(
try {
val userContext = Utils.getUserContext(app, UserHandle.getUserHandleForUid(uid))
return userContext.packageManager.getPackageInfo(
- packageName, PackageManager.GET_PERMISSIONS)
- } catch (e: PackageManager.NameNotFoundException) {}
+ packageName,
+ PackageManager.GET_PERMISSIONS
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(
+ LightPackageInfo::class.java.simpleName,
+ "Failed to get real package info for $packageName, $uid",
+ e
+ )
+ }
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 3954b7472..c1d271098 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/model/livedatatypes/LightPermInfo.kt
@@ -40,23 +40,29 @@ data class LightPermInfo(
val protectionFlags: Int,
val flags: Int
) {
- constructor (permInfo: PermissionInfo): this(permInfo.name, permInfo.packageName,
- permInfo.group, permInfo.backgroundPermission, permInfo.protection,
- permInfo.protectionFlags, permInfo.flags)
+ constructor(
+ permInfo: PermissionInfo
+ ) : this(
+ permInfo.name,
+ permInfo.packageName,
+ permInfo.group,
+ permInfo.backgroundPermission,
+ permInfo.protection,
+ permInfo.protectionFlags,
+ permInfo.flags
+ )
/**
* 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 fd7d82dfc..7492ea6e0 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 isGrantedIncludingAppOp Whether or not this permission is functionally granted.
- * A non-granted app op but granted permission is counted as not granted
+ * @param isGrantedIncludingAppOp 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) ||
(isGrantedIncludingAppOp &&
- (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) ||
+ (flags and PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0) ||
(!isGrantedIncludingAppOp &&
- (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..04cc8a796 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
+ )
}
}
}
@@ -150,9 +155,12 @@ data class LightHistoricalPackageOps(
val opEntry: AttributedOpEntry = it.getDiscreteAccessAt(i)
discreteAccessList.add(
DiscreteAccess(
+ it.opName,
opEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS),
opEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS),
- opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)))
+ opEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)
+ )
+ )
}
}
@@ -185,9 +193,12 @@ data class LightHistoricalPackageOps(
val attributedOpEntry: AttributedOpEntry = it.getDiscreteAccessAt(i)
discreteAccessList.add(
DiscreteAccess(
+ it.opName,
attributedOpEntry.getLastAccessTime(DISCRETE_ACCESS_OP_FLAGS),
attributedOpEntry.getLastDuration(DISCRETE_ACCESS_OP_FLAGS),
- attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)))
+ attributedOpEntry.getLastProxyInfo(DISCRETE_ACCESS_OP_FLAGS)
+ )
+ )
}
}
@@ -222,6 +233,7 @@ data class LightHistoricalPackageOps(
/** Data class representing a discrete permission access. */
data class DiscreteAccess(
+ val opName: String,
val accessTimeMs: Long,
val accessDurationMs: Long,
val proxy: OpEventProxyInfo?
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 ae9ccf19e..cfe753019 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/AutoRevokePermissions.kt
@@ -31,26 +31,27 @@ import com.android.permissioncontroller.DumpableLog
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.ecm.EnhancedConfirmationStatsLogUtils
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
@@ -70,7 +71,7 @@ suspend fun revokeAppPermissions(
val userManager = context.getSystemService(UserManager::class.java)
val permissionManager = context.getSystemService(PermissionManager::class.java)!!
- val splitPermissionIndex = SplitPermissionIndex(permissionManager.splitPermissions)
+ val splitPermissions = SplitPermissionIndex(permissionManager.splitPermissions)
for ((user, userApps) in apps) {
if (userManager == null || !userManager.isUserUnlocked(user)) {
@@ -78,11 +79,14 @@ 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()) {
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "${pkg.packageName}: no granted permissions")
+ }
return@forEachInParallelOuter
}
val packageName = pkg.packageName
@@ -90,15 +94,28 @@ 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 appTargetSdk = pkg.targetSdkVersion
+ val pkgPermGroups: Map<String, List<String>>? =
+ PackagePermissionsLiveData[packageName, user].getInitializedValue()
+
+ if (pkgPermGroups.isNullOrEmpty()) {
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "$packageName: no permission groups found.")
}
return@forEachInParallelOuter
}
- val targetSdk = pkg.targetSdkVersion
- val pkgPermGroups: Map<String, List<String>> =
- PackagePermissionsLiveData[packageName, user]
- .getInitializedValue() ?: return@forEachInParallelOuter
+
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "$packageName: perm groups: ${pkgPermGroups.keys}.")
+ }
// Determine which permGroups are revocable
val revocableGroups = mutableSetOf<String>()
@@ -110,116 +127,170 @@ 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.isGrantedIncludingAppOp && perm.name !in EXEMPT_PERMISSIONS
- }
- if (!fixed && granted &&
- !group.isGrantedByDefault &&
- !group.isGrantedByRole &&
- !group.isRevokeWhenRequested &&
- group.isUserSensitive) {
+ val granted =
+ group.permissions.any { (_, perm) ->
+ perm.isGrantedIncludingAppOp && perm.name !in AUTO_REVOKE_EXEMPT_PERMISSIONS
+ }
+ if (
+ !fixed &&
+ granted &&
+ !group.isGrantedByDefault &&
+ !group.isGrantedByRole &&
+ !group.isRevokeWhenRequested &&
+ group.isUserSensitive
+ ) {
revocableGroups.add(groupName)
}
}
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "$packageName: initial revocable groups: $revocableGroups")
+ }
+
// Mark any groups that split from an install-time permission as unrevocable
- for (fromPerm in
- pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()) {
- for (toGroup in
- splitPermissionIndex.getPermToGroupSplitsFrom(fromPerm, targetSdk)) {
- revocableGroups.remove(toGroup)
+ val requestedInstallPermissions =
+ pkgPermGroups[PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS] ?: emptyList()
+ for (permissionName in requestedInstallPermissions) {
+ val permissionGroups =
+ splitPermissions.getPermissionGroupsFromSplitPermission(
+ permissionName,
+ appTargetSdk
+ )
+ for (permissionGroup in permissionGroups) {
+ revocableGroups.remove(permissionGroup)
}
}
// For each unrevocable group, mark all groups that it splits from and to as unrevocable
for (groupName in pkgPermGroups.keys) {
if (!revocableGroups.contains(groupName)) {
- for (fromGroup in
- splitPermissionIndex.getGroupToGroupSplitsTo(groupName, targetSdk)) {
- revocableGroups.remove(fromGroup)
+ val sourcePermissionGroups =
+ splitPermissions.getSplitPermissionGroups(groupName, appTargetSdk)
+ for (sourcePermissionGroup in sourcePermissionGroups) {
+ revocableGroups.remove(sourcePermissionGroup)
}
- for (toGroup in
- splitPermissionIndex.getGroupToGroupSplitsFrom(groupName, targetSdk)) {
- revocableGroups.remove(toGroup)
+ val newPermissionGroups =
+ splitPermissions.getPermissionGroupsFromSplitPermissionGroup(
+ groupName,
+ appTargetSdk
+ )
+ for (permissionGroup in newPermissionGroups) {
+ revocableGroups.remove(permissionGroup)
}
}
}
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "$packageName: final revocable groups: $revocableGroups")
+ }
// For each revocable group, revoke all of its permissions
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 (revocablePermissions.isEmpty()) {
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "$packageName: revocable permissions empty")
+ }
+ return@forEachInParallelInner
+ }
- if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG,
- "revokeUnused $packageName - $revocablePermissions")
- }
+ if (DEBUG_AUTO_REVOKE) {
+ DumpableLog.i(LOG_TAG, "revokeUnused $packageName - $revocablePermissions")
+ }
- 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 uid = group.packageInfo.uid
+ for (permName in revocablePermissions) {
+ val isPackageRestrictedByEnhancedConfirmation =
+ EnhancedConfirmationStatsLogUtils.isPackageEcmRestricted(
+ context,
+ packageName,
+ uid
+ )
+ PermissionControllerStatsLog.write(
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
+ sessionId,
+ uid,
+ packageName,
+ permName,
+ false,
+ SERVER_LOG_ID,
+ /* permission_rationale_shown = */ false,
+ isPackageRestrictedByEnhancedConfirmation
+ )
+ }
- if (DEBUG_AUTO_REVOKE) {
- DumpableLog.i(LOG_TAG, "revoking $packageName - $revocablePermissions")
- DumpableLog.i(LOG_TAG, "State pre revocation: ${group.allPermissions}")
- }
- anyPermsRevoked.compareAndSet(false, true)
+ 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}")
- }
+ 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..24aab174c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/BackupHelper.java
@@ -41,6 +41,7 @@ import android.os.Build;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.permission.PermissionManager.SplitPermissionInfo;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Base64;
import android.util.Log;
@@ -48,7 +49,6 @@ import android.util.Xml;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.core.os.BuildCompat;
import com.android.permissioncontroller.Constants;
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
@@ -136,8 +136,8 @@ public class BackupHelper {
case END_TAG:
numOpenTags--;
break;
- default:
- // ignore
+ case END_DOCUMENT:
+ return;
}
}
}
@@ -324,15 +324,8 @@ public class BackupHelper {
serializer.startDocument(null, true);
serializer.startTag(null, TAG_PERMISSION_BACKUP);
-
- if (BuildCompat.isAtLeastQ()) {
- // STOPSHIP: Remove compatibility code once Q SDK level is declared
- serializer.attribute(null, ATTR_PLATFORM_VERSION,
- Integer.valueOf(Build.VERSION_CODES.Q).toString());
- } else {
- serializer.attribute(null, ATTR_PLATFORM_VERSION,
- Integer.valueOf(Build.VERSION.SDK_INT).toString());
- }
+ serializer.attribute(null, ATTR_PLATFORM_VERSION,
+ Integer.valueOf(Build.VERSION.SDK_INT).toString());
serializer.startTag(null, TAG_ALL_GRANTS);
@@ -359,6 +352,10 @@ public class BackupHelper {
*/
private void writeDelayedStorePkgsLocked(
@NonNull ArrayList<BackupPackageState> packagesToRestoreLater) {
+ if (packagesToRestoreLater.size() == 0) {
+ mContext.deleteFile(DELAYED_RESTORE_PERMISSIONS_FILE);
+ return;
+ }
try (OutputStream delayedRestoreData = mContext.openFileOutput(
DELAYED_RESTORE_PERMISSIONS_FILE, MODE_PRIVATE)) {
XmlSerializer serializer = newSerializer();
@@ -456,13 +453,18 @@ public class BackupHelper {
private final boolean mIsUserFixed;
private final boolean mWasReviewed;
+ // Not persisted, used during parsing so explicitly defined state takes precedence
+ private final boolean mIsAddedFromSplit;
+
private BackupPermissionState(@NonNull String permissionName, boolean isGranted,
- boolean isUserSet, boolean isUserFixed, boolean wasReviewed) {
+ boolean isUserSet, boolean isUserFixed, boolean wasReviewed,
+ boolean isAddedFromSplit) {
mPermissionName = permissionName;
mIsGranted = isGranted;
mIsUserSet = isUserSet;
mIsUserFixed = isUserFixed;
mWasReviewed = wasReviewed;
+ mIsAddedFromSplit = isAddedFromSplit;
}
/**
@@ -509,7 +511,8 @@ public class BackupHelper {
"true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED)),
"true".equals(parser.getAttributeValue(null, ATTR_USER_SET)),
"true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED)),
- "true".equals(parser.getAttributeValue(null, ATTR_WAS_REVIEWED))));
+ "true".equals(parser.getAttributeValue(null, ATTR_WAS_REVIEWED)),
+ /* isAddedFromSplit */ i > 0));
}
return parsedPermissions;
@@ -563,7 +566,8 @@ public class BackupHelper {
if (isNotInDefaultGrantState || perm.isUserSet() || perm.isUserFixed()
|| permissionWasReviewed) {
return new BackupPermissionState(perm.getName(), isPermGrantedIncludingAppOp(perm),
- perm.isUserSet(), perm.isUserFixed(), permissionWasReviewed);
+ perm.isUserSet(), perm.isUserFixed(), permissionWasReviewed,
+ /* isAddedFromSplit */ false);
} else {
return null;
}
@@ -763,8 +767,6 @@ public class BackupHelper {
return new BackupSigningInfoState(
currentCertDigests,
pastCertDigests);
- default:
- throw new XmlPullParserException("Could not parse signing info");
}
}
}
@@ -838,7 +840,7 @@ public class BackupHelper {
+ ATTR_PACKAGE_NAME);
}
- ArrayList<BackupPermissionState> permissionsToRestore = new ArrayList<>();
+ ArrayMap<String, BackupPermissionState> permissionsToRestore = new ArrayMap<>();
BackupSigningInfoState signingInfo = null;
while (true) {
@@ -847,7 +849,7 @@ public class BackupHelper {
switch (parser.getName()) {
case TAG_PERMISSION:
try {
- permissionsToRestore.addAll(
+ addNewPermissions(permissionsToRestore,
BackupPermissionState.parseFromXml(parser, context,
backupPlatformVersion));
} catch (XmlPullParserException e) {
@@ -876,9 +878,15 @@ public class BackupHelper {
break;
case END_TAG:
+ ArrayList<BackupPermissionState> permissionsToRestoreList =
+ new ArrayList<>();
+ int numPerms = permissionsToRestore.size();
+ for (int i = 0; i < numPerms; i++) {
+ permissionsToRestoreList.add(permissionsToRestore.valueAt(i));
+ }
return new BackupPackageState(
packageName,
- permissionsToRestore,
+ permissionsToRestoreList,
signingInfo);
case END_DOCUMENT:
throw new XmlPullParserException("Could not parse state for "
@@ -887,6 +895,26 @@ public class BackupHelper {
}
}
+ private static void addNewPermissions(
+ @NonNull ArrayMap<String, BackupPermissionState> permissionsToRestore,
+ @NonNull List<BackupPermissionState> newPermissionsToRestore) {
+ int numPerms = newPermissionsToRestore.size();
+ for (int i = 0; i < numPerms; i++) {
+ BackupPermissionState newPermission = newPermissionsToRestore.get(i);
+ boolean shouldOverwrite = true;
+ if (permissionsToRestore.containsKey(newPermission.mPermissionName)) {
+ // If it already exists only overwrite if newly added state was explicitly
+ // saved while existing state was implicit by permission split.
+ shouldOverwrite = !newPermission.mIsAddedFromSplit
+ && permissionsToRestore.get(newPermission.mPermissionName)
+ .mIsAddedFromSplit;
+ }
+ if (shouldOverwrite) {
+ permissionsToRestore.put(newPermission.mPermissionName, newPermission);
+ }
+ }
+ }
+
/**
* Get the state of a package to back up.
*
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..e258b42cd 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;
@@ -75,6 +78,7 @@ import static java.util.concurrent.TimeUnit.DAYS;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
+import android.app.Application;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -123,6 +127,8 @@ import androidx.annotation.WorkerThread;
import androidx.core.util.Preconditions;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.Constants;
+import com.android.permissioncontroller.DeviceUtils;
import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
@@ -409,30 +415,24 @@ public class LocationAccessCheck {
@WorkerThread
private void addLocationNotificationIfNeeded(@NonNull JobParameters params,
@NonNull LocationAccessCheckJobService service) {
- if (!checkLocationAccessCheckEnabledAndUpdateEnabledTime()) {
- Log.v(LOG_TAG, "LocationAccessCheck feature is not enabled.");
- service.jobFinished(params, false);
- return;
- }
-
synchronized (sLock) {
try {
if (currentTimeMillis() - mSharedPrefs.getLong(
KEY_LAST_LOCATION_ACCESS_NOTIFICATION_SHOWN, 0)
< getInBetweenNotificationsMillis()) {
- Log.v(LOG_TAG, "location notification interval is not enough.");
+ Log.i(LOG_TAG, "location notification interval is not enough.");
service.jobFinished(params, false);
return;
}
if (getCurrentlyShownNotificationLocked() != null) {
- Log.v(LOG_TAG, "already location notification exist.");
+ Log.i(LOG_TAG, "already location notification exist.");
service.jobFinished(params, false);
return;
}
addLocationNotificationIfNeeded(mAppOpsManager.getPackagesForOps(
- new String[]{OPSTR_FINE_LOCATION}));
+ new String[]{OPSTR_FINE_LOCATION}), service.getApplication());
service.jobFinished(params, false);
} catch (Exception e) {
Log.e(LOG_TAG, "Could not check for location access", e);
@@ -440,20 +440,19 @@ public class LocationAccessCheck {
} finally {
synchronized (sLock) {
service.mAddLocationNotificationIfNeededTask = null;
- Log.v(LOG_TAG, "LocationAccessCheck privacy job marked complete.");
}
}
}
}
- private void addLocationNotificationIfNeeded(@NonNull List<PackageOps> ops)
+ private void addLocationNotificationIfNeeded(@NonNull List<PackageOps> ops, Application app)
throws InterruptedException {
synchronized (sLock) {
List<UserPackage> packages = getLocationUsersLocked(ops);
ArraySet<UserPackage> alreadyNotifiedPackages = loadAlreadyNotifiedPackagesLocked();
if (DEBUG) {
- Log.v(LOG_TAG, "location packages: " + packages);
- Log.v(LOG_TAG, "already notified packages: " + alreadyNotifiedPackages);
+ Log.d(LOG_TAG, "location packages: " + packages);
+ Log.d(LOG_TAG, "already notified packages: " + alreadyNotifiedPackages);
}
throwInterruptedExceptionIfTaskIsCanceled();
// Send these issues to safety center
@@ -471,7 +470,7 @@ public class LocationAccessCheck {
if (packages.isEmpty()) {
if (DEBUG) {
- Log.v(LOG_TAG, "No package found to send a notification");
+ Log.d(LOG_TAG, "No package found to send a notification");
}
return;
}
@@ -503,7 +502,7 @@ public class LocationAccessCheck {
}
}
createPermissionReminderChannel(getUserHandleForUid(pkgInfo.applicationInfo.uid));
- createNotificationForLocationUser(pkgInfo);
+ createNotificationForLocationUser(pkgInfo, app);
}
}
@@ -583,8 +582,8 @@ public class LocationAccessCheck {
// to handle cases where the feature is remotely toggled since we don't want to
// notify for accesses before the feature was turned on.
long featureEnabledTime = getLocationAccessCheckEnabledTime();
- if (featureEnabledTime >= 0 && entry.getLastAccessBackgroundTime(
- AppOpsManager.OP_FLAGS_ALL_TRUSTED) >= featureEnabledTime) {
+ if (entry.getLastAccessBackgroundTime(AppOpsManager.OP_FLAGS_ALL_TRUSTED)
+ >= featureEnabledTime) {
pkgsWithLocationAccess.add(userPkg);
break;
}
@@ -601,36 +600,27 @@ public class LocationAccessCheck {
}
/**
- * Checks whether the location access check feature is enabled and updates the
- * time when the feature was first enabled. If the feature is enabled and no
- * enabled time persisted we persist the current time as the enabled time. If
- * the feature is disabled and an enabled time is persisted we delete the
- * persisted time.
- *
- * @return Whether the location access feature is enabled.
+ * Sets the LocationAccessCheckEnabledTime if not set.
*/
- private boolean checkLocationAccessCheckEnabledAndUpdateEnabledTime() {
- final long enabledTime = getLocationAccessCheckEnabledTime();
- if (Utils.isLocationAccessCheckEnabled()) {
- if (enabledTime <= 0) {
- mSharedPrefs.edit().putLong(KEY_LOCATION_ACCESS_CHECK_ENABLED_TIME,
- currentTimeMillis()).commit();
- }
- return true;
- } else {
- if (enabledTime > 0) {
- mSharedPrefs.edit().remove(KEY_LOCATION_ACCESS_CHECK_ENABLED_TIME)
- .commit();
- }
- return false;
+ private void setLocationAccessCheckEnabledTime() {
+ if (isLocationAccessCheckEnabledTimeNotSet()) {
+ mSharedPrefs.edit().putLong(KEY_LOCATION_ACCESS_CHECK_ENABLED_TIME,
+ currentTimeMillis()).apply();
}
}
/**
- * @return The time the location access check was enabled, or 0 if not enabled.
+ * @return true if the LocationAccessCheckEnabledTime has not been set, else false.
+ */
+ private boolean isLocationAccessCheckEnabledTimeNotSet() {
+ return mSharedPrefs.getLong(KEY_LOCATION_ACCESS_CHECK_ENABLED_TIME, 0) == 0;
+ }
+
+ /**
+ * @return The time the location access check was enabled, or currentTimeMillis if not set.
*/
private long getLocationAccessCheckEnabledTime() {
- return mSharedPrefs.getLong(KEY_LOCATION_ACCESS_CHECK_ENABLED_TIME, 0);
+ return mSharedPrefs.getLong(KEY_LOCATION_ACCESS_CHECK_ENABLED_TIME, currentTimeMillis());
}
/**
@@ -639,7 +629,7 @@ public class LocationAccessCheck {
*
* @param pkg The {@link PackageInfo} for the package to to be changed
*/
- private void createNotificationForLocationUser(@NonNull PackageInfo pkg) {
+ private void createNotificationForLocationUser(@NonNull PackageInfo pkg, Application app) {
CharSequence pkgLabel = mPackageManager.getApplicationLabel(pkg.applicationInfo);
boolean safetyCenterBgLocationReminderEnabled = isSafetyCenterBgLocationReminderEnabled();
@@ -707,20 +697,27 @@ public class LocationAccessCheck {
b.setLargeIcon(pkgIconBmp);
}
+ Bundle extras = new Bundle();
+ if (DeviceUtils.isAuto(mContext)) {
+ Bitmap settingsIcon = KotlinUtils.INSTANCE.getSettingsIcon(app, user, mPackageManager);
+ b.setLargeIcon(settingsIcon);
+ extras.putBoolean(Constants.NOTIFICATION_EXTRA_USE_LAUNCHER_ICON, false);
+ }
+
if (!TextUtils.isEmpty(appLabel)) {
- Bundle extras = new Bundle();
String appNameSubstitute = appLabel.toString();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appNameSubstitute);
- b.addExtras(extras);
}
+ b.addExtras(extras);
notificationManager.notify(pkgName, LOCATION_ACCESS_CHECK_NOTIFICATION_ID, b.build());
markAsNotified(pkgName, user, false);
- if (DEBUG) Log.i(LOG_TAG, "Notified " + pkgName);
-
- Log.v(LOG_TAG, "Location access check notification shown with sessionId=" + sessionId + ""
- + " uid=" + pkg.applicationInfo.uid + " pkgName=" + pkgName);
+ if (DEBUG) {
+ Log.d(LOG_TAG,
+ "Location access check notification shown with sessionId=" + sessionId + ""
+ + " uid=" + pkg.applicationInfo.uid + " pkgName=" + pkgName);
+ }
if (safetyCenterBgLocationReminderEnabled) {
PermissionControllerStatsLog.write(
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION,
@@ -1054,6 +1051,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);
@@ -1128,7 +1129,7 @@ public class LocationAccessCheck {
}
// Init LocationAccessCheckEnabledTime if needed
- locationAccessCheck.checkLocationAccessCheckEnabledAndUpdateEnabledTime();
+ locationAccessCheck.setLocationAccessCheckEnabledTime();
if (jobScheduler.getPendingJob(PERIODIC_LOCATION_ACCESS_CHECK_JOB_ID) == null) {
JobInfo.Builder b = (new JobInfo.Builder(PERIODIC_LOCATION_ACCESS_CHECK_JOB_ID,
@@ -1159,7 +1160,6 @@ public class LocationAccessCheck {
@Override
public void onCreate() {
- Log.v(LOG_TAG, "LocationAccessCheck privacy job is created");
super.onCreate();
mLocationAccessCheck = new LocationAccessCheck(this, () -> {
synchronized (sLock) {
@@ -1178,10 +1178,9 @@ public class LocationAccessCheck {
*/
@Override
public boolean onStartJob(JobParameters params) {
- Log.v(LOG_TAG, "LocationAccessCheck privacy job is started");
synchronized (LocationAccessCheck.sLock) {
if (mAddLocationNotificationIfNeededTask != null) {
- Log.v(LOG_TAG, "LocationAccessCheck old job not completed yet.");
+ Log.i(LOG_TAG, "LocationAccessCheck old job not completed yet.");
return false;
}
@@ -1202,7 +1201,6 @@ public class LocationAccessCheck {
*/
@Override
public boolean onStopJob(JobParameters params) {
- Log.v(LOG_TAG, "LocationAccessCheck privacy source onStopJob called.");
AddLocationNotificationIfNeededTask task;
synchronized (sLock) {
if (mAddLocationNotificationIfNeededTask == null) {
@@ -1250,7 +1248,7 @@ public class LocationAccessCheck {
long sessionId = intent.getLongExtra(EXTRA_SESSION_ID, INVALID_SESSION_ID);
int uid = intent.getIntExtra(EXTRA_UID, -1);
- Log.v(LOG_TAG,
+ Log.i(LOG_TAG,
"Location access check notification declined with sessionId=" + sessionId + ""
+ " uid=" + uid + " pkgName=" + pkg);
LocationAccessCheck locationAccessCheck = new LocationAccessCheck(context, null);
@@ -1325,7 +1323,7 @@ public class LocationAccessCheck {
UserHandle user = getParcelableExtraSafe(intent, EXTRA_USER);
long sessionId = intent.getLongExtra(EXTRA_SESSION_ID, INVALID_SESSION_ID);
int uid = intent.getIntExtra(EXTRA_UID, -1);
- Log.v(LOG_TAG,
+ Log.i(LOG_TAG,
"Location access check warning card dismissed with sessionId=" + sessionId + ""
+ " uid=" + uid + " pkgName=" + pkg);
PermissionControllerStatsLog.write(
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/PermissionControllerServiceImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java
index a7c52a531..eb78414f9 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceImpl.java
@@ -30,6 +30,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import android.Manifest;
import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -55,6 +57,7 @@ import androidx.annotation.RequiresApi;
import com.android.permissioncontroller.PermissionControllerProto.PermissionControllerDumpProto;
import com.android.permissioncontroller.PermissionControllerStatsLog;
+import com.android.permissioncontroller.ecm.EnhancedConfirmationStatsLogUtils;
import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.AppPermissions;
import com.android.permissioncontroller.permission.model.Permission;
@@ -62,6 +65,7 @@ import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGr
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState;
import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier;
import com.android.permissioncontroller.permission.utils.ArrayUtils;
+import com.android.permissioncontroller.permission.utils.ContextCompat;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.PermissionMapping;
import com.android.permissioncontroller.permission.utils.UserSensitiveFlagsUtils;
@@ -537,6 +541,17 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL
return false;
}
+ // TODO(b/333867076): Switch to !SdkLevel.isAtLeastW()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && Build.VERSION.SDK_INT <= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ RoleManager roleManager = getSystemService(RoleManager.class);
+ List<String> roleHolders =
+ roleManager.getRoleHolders(RoleManager.ROLE_SYSTEM_SUPERVISION);
+ if (roleHolders.contains(callerPackageName)) {
+ canAdminGrantSensorsPermissions = true;
+ }
+ }
+
ArrayList<String> expandedPermissions = addSplitPermissions(
Collections.singletonList(unexpandedPermission),
callerPkgInfo.applicationInfo.targetSdkVersion);
@@ -642,7 +657,14 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL
@Override
public void onOneTimePermissionSessionTimeout(@NonNull String packageName) {
- PackageManager pm = getPackageManager();
+ onOneTimePermissionSessionTimeout(packageName, ContextCompat.DEVICE_ID_DEFAULT);
+ }
+
+ @Override
+ public void onOneTimePermissionSessionTimeout(@NonNull String packageName,
+ int deviceId) {
+ Context deviceContext = ContextCompat.createDeviceContext(this, deviceId);
+ PackageManager pm = deviceContext.getPackageManager();
PackageInfo packageInfo;
int uid;
try {
@@ -656,11 +678,10 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL
if (permissions == null) {
return;
}
-
Set<AppPermissionGroup> groups = new ArraySet<>();
for (String permission : permissions) {
- AppPermissionGroup group = AppPermissionGroup.create(this, packageInfo, permission,
- true);
+ AppPermissionGroup group = AppPermissionGroup.create(deviceContext, packageInfo,
+ permission, true);
if (group != null) {
AppPermissionGroup bgGroup = group.getBackgroundPermissions();
boolean isBgGroupOneTime = bgGroup != null && bgGroup.isOneTime();
@@ -731,15 +752,18 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL
for (Permission permission : group.getPermissions()) {
if (permission.isGranted()) {
String permName = permission.getName();
- Log.v(LOG_TAG,
+ Log.i(LOG_TAG,
"Permission grant result requestId=" + requestId + " callingUid="
+ uid + " callingPackage=" + packageName + " permission="
+ permName + " isImplicit=false" + " result=" + r);
-
+ boolean isPackageRestrictedByEnhancedConfirmation =
+ EnhancedConfirmationStatsLogUtils.INSTANCE.isPackageEcmRestricted(this,
+ packageName, uid);
PermissionControllerStatsLog.write(
PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED,
requestId, uid, packageName, permName, false, r,
- /* permission_rationale_shown = */ false);
+ /* permission_rationale_shown = */ false,
+ isPackageRestrictedByEnhancedConfirmation);
}
}
}
@@ -780,13 +804,21 @@ public final class PermissionControllerServiceImpl extends PermissionControllerL
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions, @NonNull Runnable callback) {
+ onRevokeSelfPermissionsOnKill(
+ packageName, permissions, ContextCompat.DEVICE_ID_DEFAULT, callback);
+ }
+
+ @Override
+ public void onRevokeSelfPermissionsOnKill(@NonNull String packageName,
+ @NonNull List<String> permissions, int deviceId, @NonNull Runnable callback) {
+ Context deviceContext = ContextCompat.createDeviceContext(this, deviceId);
PackageInfo pkgInfo = getPkgInfo(packageName);
if (pkgInfo == null) {
throw new SecurityException("Cannot revoke permission " + String.join(",", permissions)
+ " for package " + packageName);
}
Set<AppPermissionGroup> groups = new HashSet<>();
- AppPermissions app = new AppPermissions(this, pkgInfo, false, true, null);
+ AppPermissions app = new AppPermissions(deviceContext, pkgInfo, false, true, null);
for (String permName : permissions) {
AppPermissionGroup group = app.getGroupForPermission(permName);
if (group == null) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/PermissionControllerServiceModel.kt
index 49a465898..fc812a71f 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>) {
@@ -87,27 +86,28 @@ class PermissionControllerServiceModel(private val service: PermissionController
}
var updated = false
- val observer = object : Observer<T> {
- override fun onChanged(data: T) {
- if (updated) {
- return
- }
- if ((liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) ||
- liveData !is SmartUpdateMediatorLiveData<T>) {
- onChangedFun(data)
- liveData.removeObserver(this)
- updated = true
+ val observer =
+ object : Observer<T> {
+ override fun onChanged(value: T) {
+ if (updated) {
+ return
+ }
+ if (
+ (liveData is SmartUpdateMediatorLiveData<T> && !liveData.isStale) ||
+ liveData !is SmartUpdateMediatorLiveData<T>
+ ) {
+ onChangedFun(value)
+ liveData.removeObserver(this)
+ updated = true
+ }
}
}
- }
liveData.observe(service, observer)
}
}
- /**
- * Stop observing all currently observed liveDatas
- */
+ /** Stop observing all currently observed liveDatas */
fun removeObservers() {
GlobalScope.launch(Main.immediate) {
for (liveData in observedLiveDatas) {
@@ -133,17 +133,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 +166,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 +184,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 +213,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 +247,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 +262,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++
@@ -285,31 +287,29 @@ class PermissionControllerServiceModel(private val service: PermissionController
*
* @param callback The callback our result will be returned to
*/
- fun onCountUnusedApps(
- callback: IntConsumer
- ) {
- val unusedAppsCount = Transformations.map(getUnusedPackages()) {
- it?.size ?: 0
+ fun onCountUnusedApps(callback: IntConsumer) {
+ GlobalScope.launch(Main.immediate) {
+ val unusedAppsCount = getUnusedPackages().map { it?.size ?: 0 }
+ observeAndCheckForLifecycleState(unusedAppsCount) { count ->
+ callback.accept(count ?: 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 +323,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 3405ab014..af3b60702 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/RuntimePermissionsUpgradeController.kt
@@ -28,6 +28,7 @@ import android.os.Process.myUserHandle
import android.permission.PermissionManager
import android.util.Log
import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.PermissionControllerStatsLog
import com.android.permissioncontroller.PermissionControllerStatsLog.RUNTIME_PERMISSIONS_UPGRADE_RESULT
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
@@ -42,25 +43,30 @@ 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.Utils.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT
import com.android.permissioncontroller.permission.utils.application
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
-/**
- * This class handles upgrading the runtime permissions database
- */
-internal object RuntimePermissionsUpgradeController {
+/** This class handles upgrading the runtime permissions database */
+object RuntimePermissionsUpgradeController {
private val LOG_TAG = RuntimePermissionsUpgradeController::class.java.simpleName
+ private const val DEBUG = false
// 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
+ }
+ @Suppress("MissingPermission")
fun upgradeIfNeeded(context: Context, onComplete: Runnable) {
val permissionManager = context.getSystemService(PermissionManager::class.java)
val storedVersion = permissionManager!!.runtimePermissionsVersion
@@ -69,9 +75,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 +97,6 @@ internal object RuntimePermissionsUpgradeController {
*
* @param permissionInfos permissions to exempt
* @param pkgs packages to exempt
- *
* @return the exemptions to apply
*/
private fun getExemptions(
@@ -107,9 +116,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 +126,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,207 +139,278 @@ internal object RuntimePermissionsUpgradeController {
val needBackgroundAppPermGroups = sdkUpgradedFromP && currentVersion <= 6
val needAccessMediaAppPermGroups = !isNewUser && currentVersion <= 7
val needGrantedExternalStorage = currentVersion <= 9 && SdkLevel.isAtLeastT()
+ val needBodySensorsAppPermGroups =
+ DeviceUtils.isWear(context) && 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
-
- permGroupProviders = mutableListOf()
- // Only load app-perm-groups needed for this upgrade
- if (needBackgroundAppPermGroups || needAccessMediaAppPermGroups ||
- needGrantedExternalStorage) {
- for ((pkgName, _, requestedPerms, requestedPermFlags) in
+ override fun onUpdate() {
+ if (permGroupProviders == null && pkgInfoProvider.value != null) {
+ // Second step: Trigger load of app-perm-groups
+
+ permGroupProviders = mutableSetOf()
+
+ // Only load app-perm-groups needed for this upgrade
+ if (
+ needBackgroundAppPermGroups ||
+ needAccessMediaAppPermGroups ||
+ needGrantedExternalStorage ||
+ needGrantedReadMediaVisual ||
+ needBodySensorsAppPermGroups
+ ) {
+ 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()])
- }
+ 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 (needAccessMediaAppPermGroups || needGrantedExternalStorage) {
- if (needAccessMediaAppPermGroups &&
- perm == permission.ACCESS_MEDIA_LOCATION) {
- requestsAccessMediaLocation = 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
+ }
}
- if (perm == permission.READ_EXTERNAL_STORAGE &&
- flags and PackageInfo.REQUESTED_PERMISSION_GRANTED
- != 0) {
- hasGrantedExternalStorage = true
+ if (
+ needBodySensorsAppPermGroups &&
+ perm == permission.BODY_SENSORS_BACKGROUND
+ ) {
+ permGroupProviders!!.add(
+ LightAppPermGroupLiveData[
+ pkgName, permission_group.SENSORS, 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()])
+ 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>()
+ val bgSensorsGroups = 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)
+ }
+ permission_group.SENSORS -> {
+ bgSensorsGroups.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,
+ bgSensorsGroups
+ )
}
-
- 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)
+ val upgradeData = upgradeDataProvider.getInitializedValue(forceUpdate = true)!!
// Only exempt permissions that are in the OTA. Apps that are updated via OTAs are never
// installed. Hence their permission are never exempted. This code replaces that by
// 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,
+ upgradeData.bgSensorsGroups,
+ isDeviceUpgrading
+ )
// Do not run in parallel. Measurements have shown that this is slower than sequential
for (exemption in (preinstalledAppExemptions union upgradeExemptions)) {
@@ -354,6 +430,7 @@ internal object RuntimePermissionsUpgradeController {
restrictedPermissions: Set<String>,
bgApps: List<LightAppPermGroup>,
storageAndMediaAppPermGroups: List<LightAppPermGroup>,
+ bgSensorsGroups: List<LightAppPermGroup>,
isDeviceUpgrading: Boolean
): Triple<Int, List<RestrictionExemption>, List<Grant>> {
val exemptions = mutableListOf<RestrictionExemption>()
@@ -380,9 +457,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 +480,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 +489,22 @@ internal object RuntimePermissionsUpgradeController {
val allPermissionsWithxemption = bgApp.allPermissions.toMutableMap()
allPermissionsWithxemption[permission.ACCESS_BACKGROUND_LOCATION] =
- LightPermission(perm.pkgInfo, perm.permInfo, perm.isGrantedIncludingAppOp,
+ LightPermission(
+ perm.pkgInfo,
+ perm.permInfo,
+ perm.isGrantedIncludingAppOp,
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 +520,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 +535,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 +562,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.isGrantedIncludingAppOp) {
- 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.isGrantedIncludingAppOp
+ ) {
+ 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 +594,46 @@ 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 and BODY_SENSORS 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 and BODY_SENSORS 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))
@@ -532,18 +642,86 @@ internal object RuntimePermissionsUpgradeController {
grants.add(Grant(false, visualAppPermGroup))
}
}
+
+ // Granting body sensors background permission to apps that had the pre-split body
+ // sensors permission granted.
+ Log.i(LOG_TAG, "Grandfathering body sensors background permissions")
+
+ for (bgSensorsGroup in bgSensorsGroups) {
+ val perm =
+ bgSensorsGroup.allPermissions[permission.BODY_SENSORS_BACKGROUND]
+ ?: continue
+ if (perm.flags and FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT != 0) {
+ continue
+ }
+
+ // Exempt the background permission to allow setting from users.
+ val pkgName = bgSensorsGroup.packageName
+ exemptions.add(
+ RestrictionExemption(
+ pkgName,
+ permission.BODY_SENSORS_BACKGROUND,
+ FLAG_PERMISSION_WHITELIST_UPGRADE
+ )
+ )
+
+ val allPermissionsWithExemption = bgSensorsGroup.allPermissions.toMutableMap()
+ allPermissionsWithExemption[permission.BODY_SENSORS_BACKGROUND] =
+ LightPermission(
+ perm.pkgInfo,
+ perm.permInfo,
+ perm.isGrantedIncludingAppOp,
+ perm.flags or FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+ perm.foregroundPerms
+ )
+ val group =
+ LightAppPermGroup(
+ bgSensorsGroup.packageInfo,
+ bgSensorsGroup.permGroupInfo,
+ allPermissionsWithExemption,
+ bgSensorsGroup.hasInstallToRuntimeSplit,
+ bgSensorsGroup.specialLocationGrant
+ )
+
+ // Grant the background permission only if foreground permission is granted.
+ if (group.foreground.isGranted) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "$pkgName Granted body sensors background permissions")
+ }
+ grants.add(Grant(isBackground = true, group = group))
+ }
+ }
}
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 +734,15 @@ internal object RuntimePermissionsUpgradeController {
* {@link #onUpgradeLockedDataLoaded}
*/
val bgGroups: List<LightAppPermGroup>,
+ /** Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded} */
+ val storageGroups: List<LightAppPermGroup>,
/**
- * Storage groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
+ * Background Sensors groups that need to be inspected by {@link #onUpgradeLockedDataLoaded}
*/
- val storageGroups: List<LightAppPermGroup>,
+ val bgSensorsGroups: 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 +761,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 +777,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 +809,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.i(
+ 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..2a0593057 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/SplitPermissionIndex.kt
@@ -34,18 +34,29 @@ class SplitPermissionIndex() {
constructor(splitPermissionInfos: List<PermissionManager.SplitPermissionInfo>) : this() {
val permToGroupSplits: MutableSet<SplitPermissionIndexEntry> = mutableSetOf()
val groupToGroupSplits: MutableSet<SplitPermissionIndexEntry> = mutableSetOf()
- for (splitPerm in splitPermissionInfos) {
- val oldPerm = splitPerm.splitPermission
- for (newPerm in splitPerm.newPermissions) {
- val oldPermGroup = PermissionMapping.getGroupOfPlatformPermission(oldPerm)
+ for (splitPermissionInfo in splitPermissionInfos) {
+ val splitPermission = splitPermissionInfo.splitPermission
+ for (newPerm in splitPermissionInfo.newPermissions) {
+ val splitPermissionGroup =
+ PermissionMapping.getGroupOfPlatformPermission(splitPermission)
val newPermGroup = PermissionMapping.getGroupOfPlatformPermission(newPerm)
if (newPermGroup != null) {
- permToGroupSplits.add(SplitPermissionIndexEntry(
- oldPerm, splitPerm.targetSdk, newPermGroup))
+ permToGroupSplits.add(
+ SplitPermissionIndexEntry(
+ splitPermission,
+ splitPermissionInfo.targetSdk,
+ newPermGroup
+ )
+ )
}
- if (oldPermGroup != null && newPermGroup != null) {
- groupToGroupSplits.add(SplitPermissionIndexEntry(
- oldPermGroup, splitPerm.targetSdk, newPermGroup))
+ if (splitPermissionGroup != null && newPermGroup != null) {
+ groupToGroupSplits.add(
+ SplitPermissionIndexEntry(
+ splitPermissionGroup,
+ splitPermissionInfo.targetSdk,
+ newPermGroup
+ )
+ )
}
}
}
@@ -54,38 +65,62 @@ class SplitPermissionIndex() {
}
/**
- * Given a permission, return which groups split *from* it for the given targetSdk.
+ * Given a split permission, and a package targetSdkVersion, return permission groups of new
+ * permissions. See <split-permission> tag.
+ *
+ * @param splitPermission the split permission (i.e. old permission)
+ * @param appTargetSdk app target sdk
+ * @return the permission groups calculated from new permissions
*/
- fun getPermToGroupSplitsFrom(oldPermission: String, targetSdk: Int): List<String> {
+ fun getPermissionGroupsFromSplitPermission(
+ splitPermission: String,
+ appTargetSdk: Int
+ ): List<String> {
return permToGroupSplits
- .filter { it.oldPerm == oldPermission && it.targetSdk < targetSdk }
- .map { it.newPerm }
+ .filter { it.splitPermissionOrGroup == splitPermission && appTargetSdk < it.targetSdk }
+ .map { it.newPermissionGroup }
.toList()
}
/**
- * Given a permission group, return which groups split *from* it for the given targetSdk.
+ * Given a split permission, and a package targetSdkVersion, return permission groups of new
+ * permissions. See <split-permission> tag.
+ *
+ * @param splitPermissionGroup permission group of a split permission
+ * @param appTargetSdk app target sdk
+ * @return the permission groups calculated from new permissions
*/
- fun getGroupToGroupSplitsFrom(oldPermissionGroup: String, targetSdk: Int): List<String> {
+ fun getPermissionGroupsFromSplitPermissionGroup(
+ splitPermissionGroup: String,
+ appTargetSdk: Int
+ ): List<String> {
return groupToGroupSplits
- .filter { it.oldPerm == oldPermissionGroup && it.targetSdk < targetSdk }
- .map { it.newPerm }
+ .filter {
+ it.splitPermissionOrGroup == splitPermissionGroup && appTargetSdk < it.targetSdk
+ }
+ .map { it.newPermissionGroup }
.toList()
}
/**
- * Given a permission group, return which permissions split *to* it for the given targetSdk.
+ * Given a permission group, and package's target sdk find permission groups of the split
+ * permissions, see <split-permission> tag.
+ *
+ * @param permissionGroup permission group mapped to new permissions in <split-permission> tag
+ * @param appTargetSdk app target sdk
+ * @return the permission group for the split permissions
*/
- fun getGroupToGroupSplitsTo(newPermissionGroup: String, targetSdk: Int): List<String> {
+ fun getSplitPermissionGroups(permissionGroup: String, appTargetSdk: Int): List<String> {
return groupToGroupSplits
- .filter { it.newPerm == newPermissionGroup && it.targetSdk < targetSdk }
- .map { it.oldPerm }
+ .filter { it.newPermissionGroup == permissionGroup && appTargetSdk < it.targetSdk }
+ .map { it.splitPermissionOrGroup }
.toList()
}
data class SplitPermissionIndexEntry(
- val oldPerm: String,
+ val splitPermissionOrGroup: String,
+ /** The split only applies to app target sdk below this */
val targetSdk: Int,
- val newPerm: String
+ val newPermissionGroup: String
)
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING
index b8dd3d77a..487701204 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/TEST_MAPPING
@@ -4,21 +4,24 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "include-filter": "android.permission.cts.PermissionControllerTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
- "include-filter": "android.permission.cts.OneTimePermissionTest"
+ "include-filter": "android.permission.cts.PermissionControllerTest"
},
{
- "exclude-annotation": "androidx.test.filters.FlakyTest"
+ "include-filter": "android.permission.cts.OneTimePermissionTest"
}
]
},
{
- "name": "CtsPermission3TestCases",
+ "name": "CtsPermissionUiTestCases",
"options": [
{
- "include-filter": "android.permission3.cts.SafetyLabelChangesJobServiceTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "include-filter": "android.permissionui.cts.SafetyLabelChangesJobServiceTest"
}
]
},
@@ -26,6 +29,9 @@
"name": "CtsPermissionTestCases",
"options": [
{
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
"include-filter": "android.permission.cts.LocationAccessCheckTest"
}
],
@@ -59,20 +65,57 @@
"include-annotation": "com.android.cts.devicepolicy.annotations.PermissionsTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"postsubmit": [
{
+ // TODO(b/332974906): Replace in presubmit-large.
+ "name": "CtsDevicePolicyManagerTestCases_Permissions_NoFlakes"
+ },
+ {
"name": "CtsPermissionTestCases",
"options": [
{
- "include-filter": "android.permission.cts.LocationAccessCheckTest"
+ "include-filter": "android.permission.cts.PermissionControllerTest"
},
{
- "exclude-annotation": "androidx.test.filters.FlakyTest"
+ "include-filter": "android.permission.cts.OneTimePermissionTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsPermissionUiTestCases",
+ "options": [
+ {
+ "include-filter": "android.permissionui.cts.SafetyLabelChangesJobServiceTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.LocationAccessCheckTest"
+ }
+ ],
+ "file_patterns": ["LocationAccessCheck\\.java"]
+ },
+ {
+ "name": "CtsBackupTestCases",
+ "options": [
+ {
+ "include-filter": "android.backup.cts.PermissionTest"
+ }
+ ]
+ },
+ {
+ "name": "PermissionControllerOutOfProcessTests",
+ "options": [
+ {
+ "include-filter": "com.android.permissioncontroller.tests.outofprocess.DumpTest"
}
]
}
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/v33/SafetyCenterQsTileService.kt b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt
index 6ffd894ce..a69b78a06 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v33/SafetyCenterQsTileService.kt
@@ -27,6 +27,7 @@ import android.provider.DeviceConfig
import android.safetycenter.SafetyCenterManager
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
+import android.text.TextUtils
import android.util.Log
import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.R
@@ -68,6 +69,7 @@ class SafetyCenterQsTileService : TileService() {
qsTile.label = getString(R.string.safety_privacy_qs_tile_title)
qsTile.subtitle = getString(R.string.safety_privacy_qs_tile_subtitle)
+ qsTile.contentDescription = TextUtils.concat(qsTile.label, ", ", qsTile.subtitle)
qsTile.state = Tile.STATE_ACTIVE
qsTile.updateTile()
}
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..58536b796 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/service/v34/SafetyLabelChangesJobService.kt
@@ -56,20 +56,23 @@ 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.get
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
import com.android.permissioncontroller.permission.model.v34.AppDataSharingUpdate
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.Utils.getSystemServiceSafe
+import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils
import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory
import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory.AppInfo
import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory.SafetyLabel as SafetyLabelForPersistence
import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistoryPersistence
+import com.android.permissioncontroller.safetylabel.SafetyLabelChangedBroadcastReceiver
import java.time.Duration
import java.time.Instant
import java.time.ZoneId
@@ -347,8 +350,15 @@ class SafetyLabelChangesJobService : JobService() {
} else {
context.createContextAsUser(user, 0)
}
+
+ // Asl in Apk (V+) is not supported by permissions
+ if (!SafetyLabelUtils.isAppMetadataSourceSupported(userContext, packageName)) {
+ return null
+ }
+
val appMetadataBundle: PersistableBundle =
try {
+ @Suppress("MissingPermission")
userContext.packageManager.getAppMetadata(packageName)
} catch (e: PackageManager.NameNotFoundException) {
Log.w(LOG_TAG, "Package $packageName not found while retrieving app metadata")
@@ -419,18 +429,16 @@ 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]
- .getInitializedValue(staleOk = false, forceUpdate = true)
+ .getInitializedValue(staleOk = false, forceUpdate = true)!!
.keys
private suspend fun getAllPackagesGrantedLocation(): Set<Pair<String, UserHandle>> =
SinglePermGroupPackagesUiInfoLiveData[Manifest.permission_group.LOCATION]
- .getInitializedValue(staleOk = false, forceUpdate = true)
+ .getInitializedValue(staleOk = false, forceUpdate = true)!!
.filter { (_, appPermGroupUiInfo) -> appPermGroupUiInfo.isPermissionGranted() }
.keys
@@ -439,7 +447,7 @@ class SafetyLabelChangesJobService : JobService() {
private suspend fun isSafetyLabelSupported(packageUser: Pair<String, UserHandle>): Boolean {
val lightInstallSourceInfo =
- LightInstallSourceInfoLiveData[packageUser].getInitializedValue()
+ LightInstallSourceInfoLiveData[packageUser].getInitializedValue() ?: return false
return lightInstallSourceInfo.supportsSafetyLabel
}
@@ -483,6 +491,7 @@ class SafetyLabelChangesJobService : JobService() {
val appDataSharingUpdates =
AppDataSharingUpdatesLiveData(PermissionControllerApplication.get())
.getInitializedValue()
+ ?: return 0
return appDataSharingUpdates
.map { appDataSharingUpdate ->
@@ -531,8 +540,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 =
@@ -678,6 +688,7 @@ class SafetyLabelChangesJobService : JobService() {
}
val job =
+ @Suppress("MissingPermission")
JobInfo.Builder(
SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID,
ComponentName(context, SafetyLabelChangesJobService::class.java)
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 ff63cdae5..a4f629d80 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/GrantPermissionsActivity.java
@@ -20,9 +20,10 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission_group.LOCATION;
import static android.Manifest.permission_group.READ_MEDIA_VISUAL;
-import static android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static com.android.permissioncontroller.Constants.EXTRA_IS_ECM_IN_APP;
import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED;
import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED;
import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN;
@@ -34,12 +35,16 @@ import static com.android.permissioncontroller.permission.ui.GrantPermissionsVie
import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_PERMISSION_RATIONALE;
import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.LINKED_TO_SETTINGS;
import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.APP_PERMISSION_REQUEST_CODE;
+import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.ECM_REQUEST_CODE;
import static com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.PHOTO_PICKER_REQUEST_CODE;
import static com.android.permissioncontroller.permission.utils.Utils.getRequestMessage;
+import static com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils.isDeviceAwarePermissionSupported;
import android.Manifest;
-import android.app.Activity;
+import android.annotation.SuppressLint;
import android.app.KeyguardManager;
+import android.app.ecm.EnhancedConfirmationManager;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
@@ -49,43 +54,57 @@ import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
+import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.text.Annotation;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ClickableSpan;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
-import android.view.MotionEvent;
+import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.ChecksSdkIntAtLeast;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
-import androidx.core.util.Consumer;
import androidx.core.util.Preconditions;
import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.DeviceUtils;
import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.ecm.EnhancedConfirmationStatsLogUtils;
import com.android.permissioncontroller.permission.ui.auto.GrantPermissionsAutoViewHandler;
+import com.android.permissioncontroller.permission.ui.model.DenyButton;
import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel;
import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModel.RequestInfo;
import com.android.permissioncontroller.permission.ui.model.GrantPermissionsViewModelFactory;
+import com.android.permissioncontroller.permission.ui.model.Prompt;
import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler;
+import com.android.permissioncontroller.permission.utils.ContextCompat;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
+import com.android.permissioncontroller.permission.utils.PermissionMapping;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
+import java.util.Set;
/**
* An activity which displays runtime permission prompts on behalf of an app.
@@ -97,6 +116,13 @@ public class GrantPermissionsActivity extends SettingsActivity
private static final String KEY_SESSION_ID = GrantPermissionsActivity.class.getName()
+ "_REQUEST_ID";
+ public static final String KEY_RESTRICTED_REQUESTED_PERMISSIONS =
+ GrantPermissionsActivity.class.getName() + "_RESTRICTED_REQUESTED_PERMISSIONS";
+ public static final String KEY_UNRESTRICTED_REQUESTED_PERMISSIONS =
+ GrantPermissionsActivity.class.getName() + "_UNRESTRICTED_REQUESTED_PERMISSIONS";
+ public static final String KEY_ORIGINAL_REQUESTED_PERMISSIONS =
+ GrantPermissionsActivity.class.getName() + "_ORIGINAL_REQUESTED_PERMISSIONS";
+
public static final String ANNOTATION_ID = "link";
public static final int NEXT_BUTTON = 15;
@@ -125,9 +151,14 @@ public class GrantPermissionsActivity extends SettingsActivity
public static final int DIALOG_WITH_FINE_LOCATION_ONLY = 4;
public static final int DIALOG_WITH_COARSE_LOCATION_ONLY = 5;
- public static final Map<String, Integer> PERMISSION_TO_BIT_SHIFT = Map.of(
- ACCESS_COARSE_LOCATION, 0,
- ACCESS_FINE_LOCATION, 1);
+ // The maximum number of dialogs we will allow the same package, on the same task, to launch
+ // simultaneously
+ public static final int MAX_DIALOGS_PER_PKG_TASK = 10;
+
+ public static final Map<String, Integer> PERMISSION_TO_BIT_SHIFT =
+ Map.of(
+ ACCESS_COARSE_LOCATION, 0,
+ ACCESS_FINE_LOCATION, 1);
public static final String INTENT_PHOTOS_SELECTED = "intent_extra_result";
@@ -135,8 +166,8 @@ public class GrantPermissionsActivity extends SettingsActivity
* A map of the currently shown GrantPermissionsActivity for this user, per package and task ID
*/
@GuardedBy("sCurrentGrantRequests")
- public static final Map<Pair<String, Integer>, GrantPermissionsActivity>
- sCurrentGrantRequests = new HashMap<>();
+ public static final Map<Pair<String, Integer>, GrantPermissionsActivity> sCurrentGrantRequests =
+ new HashMap<>();
/** Unique Id of a request */
private long mSessionId;
@@ -149,6 +180,22 @@ public class GrantPermissionsActivity extends SettingsActivity
/** The current list of permissions requested, across all current requests for this app */
private List<String> mRequestedPermissions = new ArrayList<>();
+
+ /**
+ * If any requested permissions are considered restricted by ECM, they will be stored here.
+ */
+ private ArrayList<String> mRestrictedRequestedPermissionGroups = null;
+
+ /**
+ * If any requested permissions are considered restricted by ECM, the non-restricted
+ * permissions will be stored here.
+ */
+ private List<String> mUnrestrictedRequestedPermissions = null;
+
+ /** A list of permissions requested on an app's behalf by the system. Usually Implicitly
+ * requested, although this isn't necessarily always the case.
+ */
+ private List<String> mSystemRequestedPermissions = new ArrayList<>();
/** A copy of the list of permissions originally requested in the intent to this activity */
private String[] mOriginalRequestedPermissions = new String[0];
@@ -157,28 +204,50 @@ public class GrantPermissionsActivity extends SettingsActivity
private List<RequestInfo> mRequestInfos = new ArrayList<>();
private GrantPermissionsViewHandler mViewHandler;
private GrantPermissionsViewModel mViewModel;
+
/**
* A list of other GrantPermissionActivities for the same package which passed their list of
* permissions to this one. They need to be informed when this activity finishes.
*/
private List<GrantPermissionsActivity> mFollowerActivities = new ArrayList<>();
+
/** Whether this activity has asked another GrantPermissionsActivity to show on its behalf */
private boolean mDelegated;
+
/** Whether this activity has been triggered by the system */
private boolean mIsSystemTriggered = false;
+
/** The set result code, or MAX_VALUE if it hasn't been set yet */
private int mResultCode = Integer.MAX_VALUE;
+
/** Package that shall have permissions granted */
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;
+ /** Which device the permission will affect. Default is the primary device. */
+ private int mTargetDeviceId = ContextCompat.DEVICE_ID_DEFAULT;
+
+ private PackageManager mPackageManager;
+
+ private ActivityResultLauncher<Intent> mShowWarningDialog =
+ registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ int resultCode = result.getResultCode();
+ if (resultCode == RESULT_OK) {
+ finishAfterTransition();
+ }
+ });
+
@Override
public void onCreate(Bundle icicle) {
+ mPackageManager = getPackageManager();
if (DeviceUtils.isAuto(this)) {
setTheme(R.style.GrantPermissions_Car_FilterTouches);
}
@@ -191,8 +260,11 @@ public class GrantPermissionsActivity extends SettingsActivity
}
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ if (DeviceUtils.isWear(this)) {
+ // Do not grab input focus and hide keyboard.
+ getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+ }
- int permissionsSdkLevel;
if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(getIntent().getAction())) {
mIsSystemTriggered = true;
mTargetPackage = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
@@ -202,9 +274,6 @@ public class GrantPermissionsActivity extends SettingsActivity
finishAfterTransition();
return;
}
- // We don't want to do any filtering in this case.
- // These calls are coming from the system on behalf of the app.
- permissionsSdkLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
} else {
// Cache this as this can only read on onCreate, not later.
mTargetPackage = getCallingPackage();
@@ -215,8 +284,7 @@ public class GrantPermissionsActivity extends SettingsActivity
return;
}
try {
- PackageInfo packageInfo = getPackageManager().getPackageInfo(mTargetPackage, 0);
- permissionsSdkLevel = packageInfo.applicationInfo.targetSdkVersion;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(mTargetPackage, 0);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Unable to get package info for the calling package.", e);
finishAfterTransition();
@@ -224,38 +292,113 @@ public class GrantPermissionsActivity extends SettingsActivity
}
}
- String[] requestedPermissionsArray = getIntent().getStringArrayExtra(
- PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ String[] requestedPermissionsArray =
+ getIntent().getStringArrayExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
if (requestedPermissionsArray == null) {
setResultAndFinish();
return;
}
- mRequestedPermissions = GrantPermissionsViewModel.Companion.getSanitizedPermissionsList(
- requestedPermissionsArray, permissionsSdkLevel);
+ mRequestedPermissions = removeNullOrEmptyPermissions(requestedPermissionsArray);
+ mOriginalRequestedPermissions = mRequestedPermissions.toArray(new String[0]);
+
+ // Do validation if permissions are requested for a remote device or the dialog is being
+ // streamed to a remote device.
+ if (isDeviceAwarePermissionSupported(getApplicationContext())) {
+ mTargetDeviceId = getIntent().getIntExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID,
+ ContextCompat.DEVICE_ID_DEFAULT);
+
+ if (mTargetDeviceId != ContextCompat.DEVICE_ID_DEFAULT) {
+ mPackageManager = ContextCompat.createDeviceContext(this, mTargetDeviceId)
+ .getPackageManager();
+ }
+
+ // When the dialog is streamed to a remote device, verify requested permissions are all
+ // device aware and target device is the same as the remote device. Otherwise show a
+ // warning dialog.
+ if (getDeviceId() != ContextCompat.DEVICE_ID_DEFAULT) {
+ boolean showWarningDialog = mTargetDeviceId != getDeviceId();
+
+ for (String permission : mRequestedPermissions) {
+ if (!MultiDeviceUtils.isPermissionDeviceAware(
+ getApplicationContext(), mTargetDeviceId, permission)) {
+ showWarningDialog = true;
+ }
+ }
+
+ if (showWarningDialog) {
+ mShowWarningDialog.launch(
+ new Intent(this, PermissionDialogStreamingBlockedActivity.class));
+ return;
+ }
+ } else if (mTargetDeviceId != ContextCompat.DEVICE_ID_DEFAULT) {
+ // On the default device, when requested permissions are for a remote device,
+ // filter out non-device aware permissions.
+ for (int i = mRequestedPermissions.size() - 1; i >= 0; i--) {
+ if (!MultiDeviceUtils.isPermissionDeviceAware(
+ getApplicationContext(),
+ mTargetDeviceId,
+ mRequestedPermissions.get(i))) {
+ Log.e(
+ LOG_TAG,
+ "non-device aware permission is requested for a remote device: "
+ + mRequestedPermissions.get(i));
+ mRequestedPermissions.remove(i);
+ }
+ }
+ }
+ }
+
if (mRequestedPermissions.isEmpty()) {
setResultAndFinish();
return;
}
- mOriginalRequestedPermissions = mRequestedPermissions.toArray(new String[0]);
+ if (mIsSystemTriggered) {
+ mSystemRequestedPermissions.addAll(mRequestedPermissions);
+ }
+
+ if (blockRestrictedPermissions(icicle)) {
+ return;
+ }
+
+ GrantPermissionsViewModelFactory factory =
+ new GrantPermissionsViewModelFactory(
+ getApplication(),
+ mTargetPackage,
+ mTargetDeviceId,
+ mRequestedPermissions,
+ mSystemRequestedPermissions,
+ mSessionId,
+ icicle);
+ mViewModel = factory.create(GrantPermissionsViewModel.class);
synchronized (sCurrentGrantRequests) {
mKey = new Pair<>(mTargetPackage, getTaskId());
- if (!sCurrentGrantRequests.containsKey(mKey)) {
+ GrantPermissionsActivity current = sCurrentGrantRequests.get(mKey);
+ if (current == null) {
sCurrentGrantRequests.put(mKey, this);
finishSystemStartedDialogsOnOtherTasksLocked();
} else if (mIsSystemTriggered) {
// The system triggered dialog doesn't require results. Delegate, and finish.
- sCurrentGrantRequests.get(mKey).onNewFollowerActivity(null,
- mRequestedPermissions);
+ current.onNewFollowerActivity(null, mRequestedPermissions, false);
finishAfterTransition();
return;
- } else if (sCurrentGrantRequests.get(mKey).mIsSystemTriggered) {
- // Normal permission requests should only merge into the system triggered dialog,
- // which has task overlay set
+ } else if (current.mIsSystemTriggered) {
+ // merge into the system triggered dialog, which has task overlay set
mDelegated = true;
- sCurrentGrantRequests.get(mKey).onNewFollowerActivity(this, mRequestedPermissions);
+ current.onNewFollowerActivity(this, mRequestedPermissions, false);
+ } else {
+ // this + current + current.mFollowerActivities
+ if ((current.mFollowerActivities.size() + 2) > MAX_DIALOGS_PER_PKG_TASK) {
+ // If there are too many dialogs for the same package, in the same task, cancel
+ finishAfterTransition();
+ return;
+ }
+ // Merge the old dialogs into the new
+ onNewFollowerActivity(current, current.mRequestedPermissions, true);
+ sCurrentGrantRequests.put(mKey, this);
}
}
@@ -277,10 +420,7 @@ public class GrantPermissionsActivity extends SettingsActivity
.GrantPermissionsViewHandlerImpl(this, this);
}
- GrantPermissionsViewModelFactory factory = new GrantPermissionsViewModelFactory(
- getApplication(), mTargetPackage, mRequestedPermissions, mSessionId, icicle);
if (!mDelegated) {
- mViewModel = factory.create(GrantPermissionsViewModel.class);
mViewModel.getRequestInfosLiveData().observe(this, this::onRequestInfoLoad);
}
@@ -315,7 +455,7 @@ public class GrantPermissionsActivity extends SettingsActivity
// as the UI behaves differently for updates and initial creations.
if (icicle != null) {
mViewHandler.loadInstanceState(icicle);
- } else {
+ } else if (mRootView == null || mRootView.getVisibility() != View.VISIBLE) {
// Do not show screen dim until data is loaded
window.setDimAmount(0f);
}
@@ -327,6 +467,121 @@ public class GrantPermissionsActivity extends SettingsActivity
}
}
+ /*
+ * Block permissions that are restricted by ECM (Enhanced Confirmation Mode).
+ *
+ * If any requested permissions are restricted, then:
+ *
+ * - Strip them from mRequestedPermissions (so no grant dialog appears for those permissions).
+ * - Group the restricted permissions into permission groups.
+ * - Show the EnhancedConfirmationDialogActivity for each group. Each showing requires a
+ * cross-activity loop during which GrantPermissionActivity will be recreated.
+ * - Finally, continue processing all non-restricted requested permissions normally
+ *
+ * Returns true if we're going to show the ECM dialog (and therefore GrantPermissionsActivity
+ * will be recreated)
+ */
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.VANILLA_ICE_CREAM, codename = "VanillaIceCream")
+ private boolean blockRestrictedPermissions(Bundle icicle) {
+ if (!SdkLevel.isAtLeastV() || !Flags.enhancedConfirmationModeApisEnabled()) {
+ return false;
+ }
+ Context userContext = Utils.getUserContext(this, Process.myUserHandle());
+ EnhancedConfirmationManager ecm = Utils.getSystemServiceSafe(userContext,
+ EnhancedConfirmationManager.class);
+
+ // Retrieve ECM-related persisted permission lists
+ if (icicle != null) {
+ mOriginalRequestedPermissions = icicle.getStringArray(
+ KEY_ORIGINAL_REQUESTED_PERMISSIONS);
+ mRestrictedRequestedPermissionGroups = icicle.getStringArrayList(
+ KEY_RESTRICTED_REQUESTED_PERMISSIONS);
+ mUnrestrictedRequestedPermissions = icicle.getStringArrayList(
+ KEY_UNRESTRICTED_REQUESTED_PERMISSIONS);
+ }
+ // If these lists aren't persisted yet, it means we haven't yet divided
+ // mRequestedPermissions into restricted-vs-unrestricted, so do so.
+ if (mRestrictedRequestedPermissionGroups == null) {
+ ArraySet<String> restrictedPermGroups = new ArraySet<>();
+ ArrayList<String> unrestrictedPermissions = new ArrayList<>();
+
+ for (String requestedPermission : mRequestedPermissions) {
+ String requestedPermGroup = PermissionMapping.getGroupOfPlatformPermission(
+ requestedPermission);
+ if (restrictedPermGroups.contains(requestedPermGroup)) {
+ continue;
+ }
+ if (requestedPermGroup != null && isPermissionEcmRestricted(ecm,
+ requestedPermission, mTargetPackage)) {
+ restrictedPermGroups.add(requestedPermGroup);
+ } else {
+ unrestrictedPermissions.add(requestedPermission);
+ }
+ }
+ mUnrestrictedRequestedPermissions = unrestrictedPermissions;
+ // If there are restricted permissions, and the ECM dialog has already been shown
+ // for this app, then we don't want to show it again. Act as if these restricted
+ // permissions weren't // requested at all, and log that we ignored them.
+ if (!restrictedPermGroups.isEmpty() && wasEcmDialogAlreadyShown(ecm, mTargetPackage)) {
+ for (String ignoredPermGroup : restrictedPermGroups) {
+ EnhancedConfirmationStatsLogUtils.INSTANCE.logDialogResultReported(
+ getPackageUid(getCallingPackage(), Process.myUserHandle()),
+ /* settingIdentifier */ ignoredPermGroup, /* firstShowForApp */ false,
+ EnhancedConfirmationStatsLogUtils.DialogResult.Suppressed);
+ }
+ mRestrictedRequestedPermissionGroups = new ArrayList<>();
+ } else {
+ mRestrictedRequestedPermissionGroups = new ArrayList<>(restrictedPermGroups);
+ }
+ }
+ // If there are remaining restricted permission groups to process, show the ECM dialog
+ // for the next one, then recreate this activity.
+ if (!mRestrictedRequestedPermissionGroups.isEmpty()) {
+ String nextRestrictedPermissionGroup = mRestrictedRequestedPermissionGroups.remove(0);
+ try {
+ Intent intent = ecm.createRestrictedSettingDialogIntent(mTargetPackage,
+ nextRestrictedPermissionGroup);
+ intent.putExtra(EXTRA_IS_ECM_IN_APP, true);
+ startActivityForResult(intent, ECM_REQUEST_CODE);
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ mRequestedPermissions = mUnrestrictedRequestedPermissions;
+ }
+ } else {
+ mRequestedPermissions = mUnrestrictedRequestedPermissions;
+ }
+ return false;
+ }
+
+ @SuppressLint("MissingPermission")
+ private int getPackageUid(String packageName, UserHandle user) {
+ try {
+ return mPackageManager.getApplicationInfoAsUser(packageName, 0, user).uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ return android.os.Process.INVALID_UID;
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private boolean isPermissionEcmRestricted(EnhancedConfirmationManager ecm,
+ String requestedPermission, String packageName) {
+ try {
+ return ecm.isRestricted(packageName, requestedPermission);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private boolean wasEcmDialogAlreadyShown(EnhancedConfirmationManager ecm,
+ String packageName) {
+ try {
+ return ecm.isClearRestrictionAllowed(packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
/**
* A new GrantPermissionsActivity has opened for this same package. Merge its requested
* permissions with the original ones set in the intent, and recalculate the grant states.
@@ -335,20 +590,41 @@ public class GrantPermissionsActivity extends SettingsActivity
* @param newPermissions The new permissions requested in the activity
*/
private void onNewFollowerActivity(@Nullable GrantPermissionsActivity follower,
- @NonNull List<String> newPermissions) {
+ @NonNull List<String> newPermissions, boolean followerIsOlder) {
if (follower != null) {
// Ensure the list of follower activities is a stack
mFollowerActivities.add(0, follower);
follower.mViewModel = mViewModel;
+ if (followerIsOlder) {
+ follower.mDelegated = true;
+ }
}
- boolean isShowingGroup = mRootView != null && mRootView.getVisibility() == View.VISIBLE;
- List<RequestInfo> currentGroups = mViewModel.getRequestInfosLiveData().getValue();
+ // If the follower is older, examine it to find the pre-merge group
+ GrantPermissionsActivity olderActivity = follower != null && followerIsOlder
+ ? follower : this;
+ boolean isShowingGroup = olderActivity.mRootView != null
+ && olderActivity.mRootView.getVisibility() == View.VISIBLE;
+ List<RequestInfo> currentGroups =
+ olderActivity.mViewModel.getRequestInfosLiveData().getValue();
if (mPreMergeShownGroupName == null && isShowingGroup
&& currentGroups != null && !currentGroups.isEmpty()) {
mPreMergeShownGroupName = currentGroups.get(0).getGroupName();
}
+ if (isShowingGroup && mPreMergeShownGroupName != null
+ && followerIsOlder && currentGroups != null) {
+ // Load a request from the old activity
+ mRequestInfos = currentGroups;
+ showNextRequest();
+ olderActivity.mRootView.setVisibility(View.GONE);
+ }
+ if (follower != null && followerIsOlder) {
+ follower.mFollowerActivities.forEach((oldFollower) ->
+ onNewFollowerActivity(oldFollower, new ArrayList<>(), true));
+ follower.mFollowerActivities.clear();
+ }
+
if (mRequestedPermissions.containsAll(newPermissions)) {
return;
}
@@ -364,9 +640,15 @@ public class GrantPermissionsActivity extends SettingsActivity
Bundle oldState = new Bundle();
mViewModel.getRequestInfosLiveData().removeObservers(this);
mViewModel.saveInstanceState(oldState);
- GrantPermissionsViewModelFactory factory = new GrantPermissionsViewModelFactory(
- getApplication(), mTargetPackage, mRequestedPermissions,
- mSessionId, oldState);
+ GrantPermissionsViewModelFactory factory =
+ new GrantPermissionsViewModelFactory(
+ getApplication(),
+ mTargetPackage,
+ mTargetDeviceId,
+ mRequestedPermissions,
+ mSystemRequestedPermissions,
+ mSessionId,
+ oldState);
mViewModel = factory.create(GrantPermissionsViewModel.class);
mViewModel.getRequestInfosLiveData().observe(this, this::onRequestInfoLoad);
if (follower != null) {
@@ -406,83 +688,60 @@ public class GrantPermissionsActivity extends SettingsActivity
}
private void showNextRequest() {
- if (mRequestInfos.isEmpty()) {
+ if (mRequestInfos.isEmpty() || mDelegated) {
return;
}
RequestInfo info = mRequestInfos.get(0);
- // Only the top activity can receive activity results
- Activity top = mFollowerActivities.isEmpty() ? this : mFollowerActivities.get(0);
- if (info.getSendToSettingsImmediately()) {
- mViewModel.sendDirectlyToSettings(top, info.getGroupName());
+ if (info.getPrompt() == Prompt.NO_UI_SETTINGS_REDIRECT) {
+ mViewModel.sendDirectlyToSettings(this, info.getGroupName());
return;
- } else if (info.getOpenPhotoPicker()) {
- mViewModel.openPhotoPicker(top, GRANTED_USER_SELECTED);
+ } else if (info.getPrompt() == Prompt.NO_UI_PHOTO_PICKER_REDIRECT) {
+ mViewModel.openPhotoPicker(this);
return;
- }
-
- if (Utils.isHealthPermissionUiEnabled() && HEALTH_PERMISSION_GROUP.equals(
- info.getGroupName())) {
- mViewModel.handleHealthConnectPermissions(top);
+ } else if (info.getPrompt() == Prompt.NO_UI_FILTER_THIS_GROUP) {
+ // Filtered permissions should be removed from the requested permissions list entirely,
+ // and not have status returned to the app
+ List<String> permissionsToFilter =
+ PermissionMapping.getPlatformPermissionNamesOfGroup(info.getGroupName());
+ mRequestedPermissions.removeAll(permissionsToFilter);
+ mRequestInfos.remove(info);
+ onRequestInfoLoad(mRequestInfos);
+ return;
+ } else if (info.getPrompt() == Prompt.NO_UI_HEALTH_REDIRECT) {
+ mViewModel.handleHealthConnectPermissions(this);
return;
}
- CharSequence appLabel = KotlinUtils.INSTANCE.getPackageLabel(getApplication(),
- mTargetPackage, Process.myUserHandle());
-
- Icon icon = null;
- int messageId = 0;
- switch(info.getMessage()) {
- case FG_MESSAGE:
- messageId = Utils.getRequest(info.getGroupName());
- break;
- case FG_FINE_LOCATION_MESSAGE:
- messageId = R.string.permgrouprequest_fineupgrade;
- break;
- case FG_COARSE_LOCATION_MESSAGE:
- messageId = R.string.permgrouprequest_coarselocation;
- break;
- case BG_MESSAGE:
- messageId = Utils.getBackgroundRequest(info.getGroupName());
- break;
- case UPGRADE_MESSAGE:
- messageId = Utils.getUpgradeRequest(info.getGroupName());
- break;
- case STORAGE_SUPERGROUP_MESSAGE_Q_TO_S:
- icon = Icon.createWithResource(getPackageName(), mStoragePermGroupIcon);
- messageId = R.string.permgrouprequest_storage_q_to_s;
- break;
- case STORAGE_SUPERGROUP_MESSAGE_PRE_Q:
- icon = Icon.createWithResource(getPackageName(), mStoragePermGroupIcon);
- messageId = R.string.permgrouprequest_storage_pre_q;
- break;
- case MORE_PHOTOS_MESSAGE:
- messageId = R.string.permgrouprequest_more_photos;
- break;
- }
-
- CharSequence message = getRequestMessage(appLabel, mTargetPackage,
- info.getGroupName(), this, messageId);
-
- int detailMessageId = 0;
- switch(info.getDetailMessage()) {
- case FG_MESSAGE:
- detailMessageId = Utils.getRequestDetail(info.getGroupName());
- break;
- case BG_MESSAGE:
- detailMessageId = Utils.getBackgroundRequestDetail(info.getGroupName());
- break;
- case UPGRADE_MESSAGE:
- detailMessageId = Utils.getUpgradeRequestDetail(info.getGroupName());
- }
-
+ String appLabel =
+ KotlinUtils.INSTANCE.getPackageLabel(
+ getApplication(), mTargetPackage, Process.myUserHandle());
+
+ // Show device name in the dialog when the dialog is streamed to a remote device OR
+ // target device is different from streamed device.
+ int dialogDisplayDeviceId = ContextCompat.getDeviceId(this);
+ boolean isMessageDeviceAware =
+ dialogDisplayDeviceId != ContextCompat.DEVICE_ID_DEFAULT
+ || dialogDisplayDeviceId != mTargetDeviceId;
+
+ int messageId = getMessageId(info.getGroupName(), info.getPrompt(), isMessageDeviceAware);
+ CharSequence message =
+ getRequestMessage(
+ appLabel,
+ mTargetPackage,
+ info.getGroupName(),
+ MultiDeviceUtils.getDeviceName(getApplicationContext(), info.getDeviceId()),
+ this,
+ isMessageDeviceAware,
+ messageId);
+
+ int detailMessageId = getDetailMessageId(info.getGroupName(), info.getPrompt());
Spanned detailMessage = null;
if (detailMessageId != 0) {
- detailMessage =
- new SpannableString(getText(detailMessageId));
- Annotation[] annotations = detailMessage.getSpans(
- 0, detailMessage.length(), Annotation.class);
+ detailMessage = new SpannableString(getText(detailMessageId));
+ Annotation[] annotations =
+ detailMessage.getSpans(0, detailMessage.length(), Annotation.class);
int numAnnotations = annotations.length;
for (int i = 0; i < numAnnotations; i++) {
Annotation annotation = annotations[i];
@@ -490,8 +749,7 @@ public class GrantPermissionsActivity extends SettingsActivity
int start = detailMessage.getSpanStart(annotation);
int end = detailMessage.getSpanEnd(annotation);
ClickableSpan clickableSpan = getLinkToAppPermissions(info);
- SpannableString spannableString =
- new SpannableString(detailMessage);
+ SpannableString spannableString = new SpannableString(detailMessage);
spannableString.setSpan(clickableSpan, start, end, 0);
detailMessage = spannableString;
break;
@@ -499,16 +757,20 @@ public class GrantPermissionsActivity extends SettingsActivity
}
}
+ Icon icon = null;
try {
- icon = icon != null ? icon : Icon.createWithResource(
- info.getGroupInfo().getPackageName(),
- info.getGroupInfo().getIcon());
+ if (info.getPrompt() == Prompt.STORAGE_SUPERGROUP_Q_TO_S
+ || info.getPrompt() == Prompt.STORAGE_SUPERGROUP_PRE_Q) {
+ icon = Icon.createWithResource(getPackageName(), mStoragePermGroupIcon);
+ } else {
+ icon = Icon.createWithResource(
+ info.getGroupInfo().getPackageName(),
+ info.getGroupInfo().getIcon());
+ }
} catch (Resources.NotFoundException e) {
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.
@@ -516,14 +778,8 @@ public class GrantPermissionsActivity extends SettingsActivity
setTitle(message);
}
- ArrayList<Integer> idxs = new ArrayList<>();
- mButtonVisibilities = new boolean[info.getButtonVisibilities().size()];
- for (int i = 0; i < info.getButtonVisibilities().size(); i++) {
- mButtonVisibilities[i] = info.getButtonVisibilities().get(i);
- if (mButtonVisibilities[i]) {
- idxs.add(i);
- }
- }
+ mButtonVisibilities = getButtonsForPrompt(info.getPrompt(), info.getDeny(),
+ info.getShowRationale());
CharSequence permissionRationaleMessage = null;
if (isPermissionRationaleVisible()) {
@@ -533,54 +789,152 @@ public class GrantPermissionsActivity extends SettingsActivity
info.getGroupName()));
}
- boolean[] locationVisibilities = new boolean[info.getLocationVisibilities().size()];
- for (int i = 0; i < info.getLocationVisibilities().size(); i++) {
- locationVisibilities[i] = info.getLocationVisibilities().get(i);
- }
+ boolean[] locationVisibilities = getLocationButtonsForPrompt(info.getPrompt());
if (mRequestCounts < mRequestInfos.size()) {
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);
+ if (mIsSystemTriggered) {
+ // We don't want the keyboard obscuring system-triggered dialogs
+ InputMethodManager manager = getSystemService(InputMethodManager.class);
+ manager.hideSoftInputFromWindow(mRootView.getWindowToken(), 0);
+ }
mRootView.setVisibility(View.VISIBLE);
}
}
- // LINT.IfChange(dispatchTouchEvent)
+ private int getMessageId(String permGroupName, Prompt prompt, Boolean isDeviceAware) {
+ return switch (prompt) {
+ case UPGRADE_SETTINGS_LINK, OT_UPGRADE_SETTINGS_LINK -> Utils.getUpgradeRequest(
+ permGroupName, isDeviceAware);
+ case SETTINGS_LINK_FOR_BG, SETTINGS_LINK_WITH_OT -> Utils.getBackgroundRequest(
+ permGroupName, isDeviceAware);
+ case LOCATION_FINE_UPGRADE -> Utils.getFineLocationRequest(isDeviceAware);
+ case LOCATION_COARSE_ONLY -> Utils.getCoarseLocationRequest(isDeviceAware);
+ case STORAGE_SUPERGROUP_PRE_Q -> R.string.permgrouprequest_storage_pre_q;
+ case STORAGE_SUPERGROUP_Q_TO_S -> R.string.permgrouprequest_storage_q_to_s;
+ case SELECT_MORE_PHOTOS -> Utils.getMorePhotosRequest(isDeviceAware);
+ default -> Utils.getRequest(permGroupName, isDeviceAware);
+ };
+ }
+
+ private int getDetailMessageId(String permGroupName, Prompt prompt) {
+ return switch (prompt) {
+ case UPGRADE_SETTINGS_LINK, OT_UPGRADE_SETTINGS_LINK ->
+ Utils.getUpgradeRequestDetail(permGroupName);
+ case SETTINGS_LINK_FOR_BG, SETTINGS_LINK_WITH_OT ->
+ Utils.getBackgroundRequestDetail(permGroupName);
+ default -> 0;
+ };
+ }
+
+ private boolean[] getButtonsForPrompt(Prompt prompt, DenyButton denyButton,
+ boolean shouldShowRationale) {
+ ArraySet<Integer> buttons = new ArraySet<>();
+ switch (prompt) {
+ case BASIC, STORAGE_SUPERGROUP_PRE_Q, STORAGE_SUPERGROUP_Q_TO_S ->
+ buttons.add(ALLOW_BUTTON);
+ case FG_ONLY, SETTINGS_LINK_FOR_BG -> buttons.add(ALLOW_FOREGROUND_BUTTON);
+ case ONE_TIME_FG, SETTINGS_LINK_WITH_OT, LOCATION_TWO_BUTTON_COARSE_HIGHLIGHT,
+ LOCATION_TWO_BUTTON_FINE_HIGHLIGHT, LOCATION_COARSE_ONLY,
+ LOCATION_FINE_UPGRADE ->
+ buttons.addAll(Arrays.asList(ALLOW_FOREGROUND_BUTTON, ALLOW_ONE_TIME_BUTTON));
+ case SELECT_PHOTOS, SELECT_MORE_PHOTOS ->
+ buttons.addAll(Arrays.asList(ALLOW_ALL_BUTTON, ALLOW_SELECTED_BUTTON));
+ }
+
+ switch (denyButton) {
+ case DENY -> buttons.add(DENY_BUTTON);
+ case DENY_DONT_ASK_AGAIN -> buttons.add(DENY_AND_DONT_ASK_AGAIN_BUTTON);
+ case DONT_SELECT_MORE -> buttons.add(DONT_ALLOW_MORE_SELECTED_BUTTON);
+ case NO_UPGRADE -> buttons.add(NO_UPGRADE_BUTTON);
+ case NO_UPGRADE_OT -> buttons.add(NO_UPGRADE_OT_BUTTON);
+ case NO_UPGRADE_AND_DONT_ASK_AGAIN ->
+ buttons.add(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON);
+ case NO_UPGRADE_AND_DONT_ASK_AGAIN_OT ->
+ buttons.add(NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON);
+ }
+
+ if (shouldShowRationale) {
+ buttons.add(LINK_TO_PERMISSION_RATIONALE);
+ }
+ return convertSetToBoolList(buttons, NEXT_BUTTON);
+ }
+
+ private boolean[] getLocationButtonsForPrompt(Prompt prompt) {
+ ArraySet<Integer> locationButtons = new ArraySet<>();
+ switch (prompt) {
+ case LOCATION_TWO_BUTTON_COARSE_HIGHLIGHT ->
+ locationButtons.addAll(Arrays.asList(LOCATION_ACCURACY_LAYOUT,
+ DIALOG_WITH_BOTH_LOCATIONS, COARSE_RADIO_BUTTON));
+ case LOCATION_TWO_BUTTON_FINE_HIGHLIGHT ->
+ locationButtons.addAll(Arrays.asList(LOCATION_ACCURACY_LAYOUT,
+ DIALOG_WITH_BOTH_LOCATIONS, FINE_RADIO_BUTTON));
+ case LOCATION_COARSE_ONLY ->
+ locationButtons.addAll(Arrays.asList(LOCATION_ACCURACY_LAYOUT,
+ DIALOG_WITH_COARSE_LOCATION_ONLY));
+ case LOCATION_FINE_UPGRADE ->
+ locationButtons.addAll(Arrays.asList(LOCATION_ACCURACY_LAYOUT,
+ DIALOG_WITH_FINE_LOCATION_ONLY));
+ }
+ return convertSetToBoolList(locationButtons, NEXT_LOCATION_DIALOG);
+ }
+
+ private boolean[] convertSetToBoolList(Set<Integer> buttonSet, int size) {
+ boolean[] buttonArray = new boolean[size];
+ for (int button: buttonSet) {
+ buttonArray[button] = true;
+ }
+ return buttonArray;
+ }
+
@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();
- }
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ESCAPE
+ && event.getRepeatCount() == 0
+ && event.hasNoModifiers()) {
+ event.startTracking();
+ mViewHandler.onCancelled();
finishAfterTransition();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ESCAPE
+ && event.isTracking()
+ && !event.isCanceled()) {
+ // Mark it as handled since we did handle the down event
+ return true;
}
- return super.dispatchTouchEvent(ev);
+ return super.onKeyUp(keyCode, event);
}
- // LINT.ThenChange(PermissionRationaleActivity.java:dispatchTouchEvent)
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
+ if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) {
+ outState.putStringArrayList(KEY_RESTRICTED_REQUESTED_PERMISSIONS,
+ mRestrictedRequestedPermissionGroups != null ? new ArrayList<>(
+ mRestrictedRequestedPermissionGroups) : null);
+ outState.putStringArrayList(KEY_UNRESTRICTED_REQUESTED_PERMISSIONS,
+ mUnrestrictedRequestedPermissions != null ? new ArrayList<>(
+ mUnrestrictedRequestedPermissions) : null);
+ outState.putStringArray(KEY_ORIGINAL_REQUESTED_PERMISSIONS,
+ mOriginalRequestedPermissions);
+ }
+
if (mViewHandler == null || mViewModel == null) {
return;
}
@@ -606,16 +960,20 @@ 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 (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) {
+ if (requestCode == ECM_REQUEST_CODE) {
+ recreate();
+ return;
+ }
+ }
+ 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
@@ -635,18 +993,14 @@ 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(top, result);
+ if (Objects.equals(READ_MEDIA_VISUAL, name) && result == GRANTED_USER_SELECTED) {
+ mViewModel.openPhotoPicker(this);
logGrantPermissionActivityButtons(name, affectedForegroundPermissions, result);
return;
}
logGrantPermissionActivityButtons(name, affectedForegroundPermissions, result);
mViewModel.onPermissionGrantResult(name, affectedForegroundPermissions, result);
- showNextRequest();
if (result == CANCELED) {
setResultAndFinish();
}
@@ -740,14 +1094,16 @@ public class GrantPermissionsActivity extends SettingsActivity
private boolean setResultIfNeeded(int resultCode) {
if (!isResultSet()) {
List<String> oldRequestedPermissions = mRequestedPermissions;
+ mResultCode = resultCode;
removeActivityFromMap();
// If a new merge request came in before we managed to remove this activity from the
// map, then cancel the result set for now.
if (!Objects.equals(oldRequestedPermissions, mRequestedPermissions)) {
+ // Reset the result code back to its starting value of MAX_VALUE;
+ mResultCode = Integer.MAX_VALUE;
return false;
}
- mResultCode = resultCode;
if (mViewModel != null) {
mViewModel.logRequestedPermissionGroups();
}
@@ -759,9 +1115,9 @@ public class GrantPermissionsActivity extends SettingsActivity
if ((mDelegated || (mViewModel != null && mViewModel.shouldReturnPermissionState()))
&& mTargetPackage != null) {
- PackageManager pm = getPackageManager();
for (int i = 0; i < resultPermissions.length; i++) {
- grantResults[i] = pm.checkPermission(resultPermissions[i], mTargetPackage);
+ grantResults[i] =
+ mPackageManager.checkPermission(resultPermissions[i], mTargetPackage);
}
} else {
grantResults = new int[0];
@@ -770,6 +1126,10 @@ public class GrantPermissionsActivity extends SettingsActivity
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, resultPermissions);
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults);
+ if (isDeviceAwarePermissionSupported(this)) {
+ result.putExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, mTargetDeviceId);
+ }
result.putExtra(Intent.EXTRA_PACKAGE_NAME, mTargetPackage);
setResult(resultCode, result);
}
@@ -877,6 +1237,18 @@ public class GrantPermissionsActivity extends SettingsActivity
return mResultCode != Integer.MAX_VALUE;
}
+ // Remove null and empty permissions from an array, return a list
+ private List<String> removeNullOrEmptyPermissions(String[] perms) {
+ ArrayList<String> sanitized = new ArrayList<>();
+ for (String perm : perms) {
+ if (perm == null || perm.isEmpty()) {
+ continue;
+ }
+ sanitized.add(perm);
+ }
+ return sanitized;
+ }
+
/**
* If there is another system-shown dialog on another task, that is not being relied upon by an
* app-defined dialogs, these other dialogs should be finished.
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/LocationProviderInterceptDialog.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/LocationProviderInterceptDialog.java
index bd3cd1e22..88915edfd 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/LocationProviderInterceptDialog.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/LocationProviderInterceptDialog.java
@@ -23,6 +23,7 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
+import android.view.Window;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -48,18 +49,34 @@ public final class LocationProviderInterceptDialog extends FragmentActivity {
return;
}
- new AlertDialog.Builder(this)
- .setIcon(R.drawable.ic_dialog_alert_material)
- .setTitle(android.R.string.dialog_alert_title)
- .setMessage(getString(R.string.location_warning,
- Utils.getAppLabel(getPackageInfo(packageName).applicationInfo, this)))
- .setNegativeButton(R.string.ok, null)
- .setPositiveButton(R.string.location_settings, (dialog, which) ->
- startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)))
- .setOnDismissListener((dialog) -> finish())
- .show();
+ AlertDialog alertDialog =
+ new AlertDialog.Builder(this)
+ .setIcon(R.drawable.ic_dialog_alert_material)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setMessage(
+ getString(
+ R.string.location_warning,
+ Utils.getAppLabel(
+ getPackageInfo(packageName).applicationInfo, this)))
+ .setNegativeButton(R.string.ok, null)
+ .setPositiveButton(
+ R.string.location_settings,
+ (dialog, which) ->
+ startActivity(
+ new Intent(
+ Settings.ACTION_LOCATION_SOURCE_SETTINGS)))
+ .setOnDismissListener((dialog) -> finish())
+ .show();
+ try {
+ Window alertWindow = alertDialog.getWindow();
+ if (alertWindow != null) {
+ alertWindow.getDecorView().requestFocus();
+ }
+ } catch (Exception ignored) {
+ }
}
+
private @Nullable PackageInfo getPackageInfo(@NonNull String packageName) {
try {
return getPackageManager().getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
index afeb19aa9..df5b98ae7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/ManagePermissionsActivity.java
@@ -31,7 +31,7 @@ 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.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -71,14 +71,16 @@ import com.android.permissioncontroller.permission.ui.auto.dashboard.AutoPermiss
import com.android.permissioncontroller.permission.ui.auto.dashboard.AutoPermissionUsageFragment;
import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment;
import com.android.permissioncontroller.permission.ui.handheld.AppPermissionGroupsFragment;
-import com.android.permissioncontroller.permission.ui.handheld.HandheldUnusedAppsWrapperFragment;
import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment;
import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionDetailsWrapperFragment;
import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageWrapperFragment;
import com.android.permissioncontroller.permission.ui.handheld.v34.AppDataSharingUpdatesFragment;
import com.android.permissioncontroller.permission.ui.legacy.AppPermissionActivity;
import com.android.permissioncontroller.permission.ui.television.TvUnusedAppsFragment;
-import com.android.permissioncontroller.permission.ui.wear.AppPermissionsFragmentWear;
+import com.android.permissioncontroller.permission.ui.wear.WearAppPermissionFragment;
+import com.android.permissioncontroller.permission.ui.wear.WearPermissionUsageDetailsFragment;
+import com.android.permissioncontroller.permission.ui.wear.WearPermissionUsageFragment;
+import com.android.permissioncontroller.permission.ui.wear.WearUnusedAppsFragment;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.PermissionMapping;
import com.android.permissioncontroller.permission.utils.Utils;
@@ -138,11 +140,12 @@ public final class ManagePermissionsActivity extends SettingsActivity {
*/
private static final int PROXY_ACTIVITY_REQUEST_CODE = 5;
+ @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
private static final String LAUNCH_PERMISSION_SETTINGS =
- "android.permission.LAUNCH_PERMISSION_SETTINGS";
+ Manifest.permission.LAUNCH_PERMISSION_SETTINGS;
- private static final String APP_PERMISSIONS_SETTINGS =
- "android.settings.APP_PERMISSIONS_SETTINGS";
+ @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static final String APP_PERMISSIONS_SETTINGS = Settings.ACTION_APP_PERMISSIONS_SETTINGS;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -153,10 +156,13 @@ public final class ManagePermissionsActivity extends SettingsActivity {
}
super.onCreate(savedInstanceState);
- // If this is not a phone (which uses the Navigation component), and there is a previous
- // instance, re-use its Fragment instead of making a new one.
- if ((DeviceUtils.isTelevision(this) || DeviceUtils.isAuto(this)
- || DeviceUtils.isWear(this)) && savedInstanceState != null) {
+ // TODO(b/309578419): Make this activity handle insets properly and then remove this.
+ getTheme().applyStyle(R.style.OptOutEdgeToEdgeEnforcement, /* force */ false);
+
+ // If this is not a phone or a watch (which uses the Navigation component), and there
+ // is a previous instance, re-use its Fragment instead of making a new one.
+ if ((DeviceUtils.isTelevision(this) || DeviceUtils.isAuto(this))
+ && savedInstanceState != null) {
return;
}
@@ -228,6 +234,8 @@ public final class ManagePermissionsActivity extends SettingsActivity {
PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__OPEN);
if (DeviceUtils.isAuto(this)) {
androidXFragment = new AutoPermissionUsageFragment();
+ } else if (DeviceUtils.isWear(this)) {
+ androidXFragment = WearPermissionUsageFragment.newInstance(sessionId);
} else {
androidXFragment = PermissionUsageWrapperFragment.newInstance(
Long.MAX_VALUE, sessionId);
@@ -249,6 +257,9 @@ public final class ManagePermissionsActivity extends SettingsActivity {
if (DeviceUtils.isAuto(this)) {
androidXFragment = AutoPermissionUsageDetailsFragment.Companion.newInstance(
groupName, showSystem, sessionId);
+ } else if (DeviceUtils.isWear(this)) {
+ androidXFragment = WearPermissionUsageDetailsFragment
+ .newInstance(groupName, showSystem, show7Days);
} else {
androidXFragment = PermissionDetailsWrapperFragment
.newInstance(groupName, Long.MAX_VALUE, showSystem, sessionId,
@@ -258,8 +269,7 @@ public final class ManagePermissionsActivity extends SettingsActivity {
}
case Intent.ACTION_MANAGE_APP_PERMISSION: {
- if (DeviceUtils.isAuto(this) || DeviceUtils.isTelevision(this)
- || DeviceUtils.isWear(this)) {
+ if (DeviceUtils.isAuto(this) || DeviceUtils.isTelevision(this)) {
Intent compatIntent = new Intent(this, AppPermissionActivity.class);
compatIntent.putExtras(getIntent().getExtras());
startActivityForResult(compatIntent, PROXY_ACTIVITY_REQUEST_CODE);
@@ -297,15 +307,22 @@ public final class ManagePermissionsActivity extends SettingsActivity {
return;
}
- Bundle args = AppPermissionFragment.createArgs(packageName, permissionName,
- groupName, userHandle, caller, sessionId, null);
+ Bundle args;
+ if (DeviceUtils.isWear(this)) {
+ args = WearAppPermissionFragment.createArgs(packageName, permissionName,
+ groupName, userHandle, caller, sessionId, null);
+ } else {
+ args = AppPermissionFragment.createArgs(packageName, permissionName,
+ groupName, userHandle, caller, sessionId, null);
+ }
setNavGraph(args, R.id.app_permission);
return;
}
case Intent.ACTION_MANAGE_APP_PERMISSIONS:
case APP_PERMISSIONS_SETTINGS: {
- if (Objects.equals(action, APP_PERMISSIONS_SETTINGS)) {
+ if (!SdkLevel.isAtLeastV()
+ && Objects.equals(action, APP_PERMISSIONS_SETTINGS)) {
PermissionInfo permissionInfo;
try {
permissionInfo = getPackageManager()
@@ -374,8 +391,6 @@ public final class ManagePermissionsActivity extends SettingsActivity {
androidXFragment = AutoAppPermissionsFragment.newInstance(packageName,
userHandle, sessionId, /* isSystemPermsScreen= */ true);
}
- } else if (DeviceUtils.isWear(this)) {
- androidXFragment = AppPermissionsFragmentWear.newInstance(packageName);
} else if (DeviceUtils.isTelevision(this)) {
androidXFragment = com.android.permissioncontroller.permission.ui.television
.AppPermissionsFragment.newInstance(packageName, userHandle);
@@ -473,8 +488,8 @@ public final class ManagePermissionsActivity extends SettingsActivity {
androidXFragment = TvUnusedAppsFragment.newInstance();
androidXFragment.setArguments(UnusedAppsFragment.createArgs(sessionId));
} else if (DeviceUtils.isWear(this)) {
- androidXFragment = HandheldUnusedAppsWrapperFragment.newInstance();
- androidXFragment.setArguments(UnusedAppsFragment.createArgs(sessionId));
+ setNavGraph(WearUnusedAppsFragment.createArgs(sessionId), R.id.auto_revoke);
+ return;
} else {
setNavGraph(UnusedAppsFragment.createArgs(sessionId), R.id.auto_revoke);
return;
@@ -549,15 +564,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/PermissionDialogStreamingBlockedActivity.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/PermissionDialogStreamingBlockedActivity.kt
new file mode 100644
index 000000000..29f9ed2c7
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/PermissionDialogStreamingBlockedActivity.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui
+
+import android.app.AlertDialog
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import com.android.permissioncontroller.R
+
+/**
+ * In some scenarios we want to prevent the permission grant dialog from streaming to a remote
+ * device. If the streaming is blocked show a warning dialog rendered by this activity.
+ */
+class PermissionDialogStreamingBlockedActivity : FragmentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ AlertDialog.Builder(this)
+ .setTitle(R.string.permission_grant_dialog_streaming_blocked_title)
+ .setMessage(R.string.permission_grant_dialog_streaming_blocked_description)
+ .setPositiveButton(R.string.ongoing_usage_dialog_ok, null)
+ .setOnDismissListener() {
+ setResult(RESULT_OK)
+ finish()
+ }
+ .create()
+ .show()
+ }
+}
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/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING
index de4c3a2b9..d0b115bcb 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/TEST_MAPPING
@@ -5,6 +5,9 @@
"options": [
{
"include-filter": "android.permission.cts.OneTimePermissionTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
@@ -17,14 +20,26 @@
"include-annotation": "com.android.cts.devicepolicy.annotations.PermissionsTest"
},
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"postsubmit": [
{
- "name": "CtsPermission3TestCases"
+ "name": "CtsPermissionUiTestCases"
+ },
+ {
+ // TODO(b/332974906): Promote in presubmit-large.
+ "name": "CtsDevicePolicyManagerTestCases_Permissions_NoFlakes"
+ },
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.OneTimePermissionTest"
+ }
+ ]
}
]
}
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..1f30572ad 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionFragment.java
@@ -25,6 +25,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT;
+import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -33,6 +34,8 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.hardware.SensorPrivacyManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.BidiFormatter;
@@ -42,6 +45,7 @@ import android.widget.RadioButton;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.core.content.res.TypedArrayUtils;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
@@ -53,6 +57,8 @@ import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
import com.android.car.ui.AlertDialogBuilder;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.flags.Flags;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment;
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler;
@@ -61,19 +67,25 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod
import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory;
import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
+import com.android.permissioncontroller.permission.utils.LocationUtils;
import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor;
+import com.android.permissioncontroller.permission.utils.Utils;
import com.android.settingslib.RestrictedLockUtils;
-import java.util.Map;
-
import kotlin.Pair;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
/** Settings related to a particular permission for the given app. */
public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
implements AppPermissionViewModel.ConfirmDialogShowingFragment {
private static final String LOG_TAG = "AppPermissionFragment";
private static final long POST_DELAY_MS = 20;
+ private static final String BLOCKED_APP_PREF_KEY = "blocked_app";
+ private static final String REQUIRED_APP_PREF_KEY = "required_app";
@NonNull
private TwoStatePreference mAllowPermissionPreference;
@@ -100,6 +112,10 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
private String mPermGroupLabel;
private Drawable mPackageIcon;
+ private SensorPrivacyManager mSensorPrivacyManager;
+ private Collection<String> mAutomotiveLocationBypassAllowlist;
+ private List<String> mCameraPrivacyAllowlist;
+
/**
* Listens for changes to the app the permission is currently getting granted to. {@code null}
* when unregistered.
@@ -156,6 +172,14 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
mPackageName, mUser);
setHeaderLabel(
requireContext().getString(R.string.app_permission_title, mPermGroupLabel));
+ if (SdkLevel.isAtLeastV()) {
+ mSensorPrivacyManager = requireContext().getSystemService(SensorPrivacyManager.class);
+ mCameraPrivacyAllowlist = mSensorPrivacyManager.getCameraPrivacyAllowlist();
+ if (Flags.addBannersToPrivacySensitiveAppsForAaos()) {
+ mAutomotiveLocationBypassAllowlist =
+ LocationUtils.getAutomotiveLocationBypassAllowlist(requireContext());
+ }
+ }
}
@Override
@@ -257,6 +281,19 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
mViewModel.getButtonStateLiveData().observe(this, this::setRadioButtonsState);
mViewModel.getDetailResIdLiveData().observe(this, this::setDetail);
mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail);
+ if (SdkLevel.isAtLeastV()) {
+ if (Manifest.permission_group.CAMERA.equals(mPermGroupName)) {
+ mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus);
+ }
+ if (Flags.addBannersToPrivacySensitiveAppsForAaos()) {
+ if (Manifest.permission_group.LOCATION.equals(mPermGroupName)) {
+ mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus);
+ }
+ if (Manifest.permission_group.MICROPHONE.equals(mPermGroupName)) {
+ mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus);
+ }
+ }
+ }
}
@Override
@@ -269,6 +306,122 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
}
}
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private void setSensorStatus(Boolean sensorStatus) {
+ Boolean isRequiredApp = null;
+ Boolean isRequiredAppCard = null;
+ if (Manifest.permission_group.CAMERA.equals(mPermGroupName)) {
+ int state = mSensorPrivacyManager.getSensorPrivacyState(
+ SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
+ SensorPrivacyManager.Sensors.CAMERA);
+ isRequiredApp = mCameraPrivacyAllowlist.contains(mPackageName);
+ isRequiredAppCard =
+ state == SensorPrivacyManager.StateTypes.ENABLED_EXCEPT_ALLOWLISTED_APPS
+ && isRequiredApp;
+ } else if (Manifest.permission_group.LOCATION.equals(mPermGroupName)) {
+ isRequiredApp = mAutomotiveLocationBypassAllowlist.contains(mPackageName);
+ isRequiredAppCard =
+ isRequiredApp && LocationUtils.isAutomotiveLocationBypassEnabled(
+ getPreferenceManager().getContext());
+ } else if (Manifest.permission_group.MICROPHONE.equals(mPermGroupName)) {
+ isRequiredApp = false;
+ isRequiredAppCard = false;
+ }
+
+ if (isRequiredApp != null && isRequiredAppCard != null) {
+ if (sensorStatus) {
+ setSensorCard(isRequiredAppCard, isRequiredApp);
+ } else {
+ removeSensorCard(isRequiredAppCard);
+ }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private void setSensorCard(boolean isRequiredAppCard, boolean isRequiredApp) {
+ if (isRequiredAppCard) {
+ setRequiredAppCard();
+ } else {
+ setBlockedAppCard(isRequiredApp);
+ }
+ }
+
+ private void setRequiredAppCard() {
+ AutoCardViewPreference sensorCard = findPreference(REQUIRED_APP_PREF_KEY);
+ if (sensorCard == null) {
+ sensorCard = createRequiredAppCard();
+ if (getPreferenceScreen() != null) {
+ getPreferenceScreen().addPreference(sensorCard);
+ }
+ }
+ sensorCard.setVisible(true);
+ }
+
+ private void setBlockedAppCard(boolean isRequiredApp) {
+ AutoCardViewPreference sensorCard = findPreference(BLOCKED_APP_PREF_KEY);
+ if (sensorCard == null) {
+ sensorCard = createBlockedAppCard(isRequiredApp);
+ if (getPreferenceScreen() != null) {
+ getPreferenceScreen().addPreference(sensorCard);
+ }
+ }
+ sensorCard.setVisible(true);
+ }
+
+ private AutoCardViewPreference createRequiredAppCard() {
+ Context context = getPreferenceManager().getContext();
+ AutoCardViewPreference sensorCard = new AutoCardViewPreference(context);
+ sensorCard.setKey(REQUIRED_APP_PREF_KEY);
+ sensorCard.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, mPermGroupName));
+ sensorCard.setTitle(context.getString(R.string.automotive_required_app_title));
+ sensorCard.setSummary(context.getString(R.string.automotive_required_app_summary));
+ sensorCard.setVisible(true);
+ sensorCard.setOrder(-1);
+ return sensorCard;
+ }
+
+ private AutoCardViewPreference createBlockedAppCard(boolean isRequiredApp) {
+ Context context = getPreferenceManager().getContext();
+
+ AutoCardViewPreference sensorCard = new AutoCardViewPreference(context);
+ sensorCard.setKey(BLOCKED_APP_PREF_KEY);
+ sensorCard.setIcon(Utils.getBlockedIcon(mPermGroupName));
+ sensorCard.setTitle(context.getString(Utils.getBlockedTitleAutomotive(mPermGroupName)));
+ if (isRequiredApp) {
+ sensorCard.setSummary(context.getString(
+ R.string.automotive_blocked_required_app_summary));
+ } else {
+ sensorCard.setSummary(context.getString(
+ R.string.automotive_blocked_infotainment_app_summary));
+ }
+ sensorCard.setVisible(true);
+ sensorCard.setOrder(-1);
+ return sensorCard;
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private void removeSensorCard(boolean isRequiredAppCard) {
+ if (isRequiredAppCard) {
+ removeRequiredAppCard();
+ } else {
+ removeBlockedAppCard();
+ }
+ }
+
+ private void removeRequiredAppCard() {
+ AutoCardViewPreference sensorCard = findPreference(REQUIRED_APP_PREF_KEY);
+ if (sensorCard != null) {
+ sensorCard.setVisible(false);
+ }
+ }
+
+ private void removeBlockedAppCard() {
+ AutoCardViewPreference sensorCard = findPreference(BLOCKED_APP_PREF_KEY);
+ if (sensorCard != null) {
+ sensorCard.setVisible(false);
+ }
+ }
+
@Override
public void showConfirmDialog(ChangeRequest changeRequest, int messageId,
int buttonPressed, boolean oneTime) {
@@ -382,8 +535,9 @@ public class AutoAppPermissionFragment extends AutoSettingsFrameFragment
private static class SelectedPermissionPreference extends TwoStatePreference {
SelectedPermissionPreference(Context context) {
- super(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
- android.R.attr.preferenceStyle));
+ super(context, null,
+ TypedArrayUtils.getAttr(context, androidx.preference.R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
setPersistent(false);
setLayoutResource(R.layout.car_radio_button_preference);
setWidgetLayoutResource(R.layout.radio_button_preference_widget);
@@ -418,7 +572,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/AutoAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
index cf58f1e17..446d97c3e 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoAppPermissionsFragment.java
@@ -400,7 +400,7 @@ public class AutoAppPermissionsFragment extends AutoSettingsFrameFragment implem
}
PermissionControllerStatsLog.write(APP_PERMISSIONS_FRAGMENT_VIEWED, sessionId, viewId,
permissionGroupName, uid, mPackageName, category);
- Log.v(LOG_TAG, "AutoAppPermissionFragment view logged with sessionId=" + sessionId
+ Log.i(LOG_TAG, "AutoAppPermissionFragment view logged with sessionId=" + sessionId
+ " viewId=" + viewId + " permissionGroupName=" + permissionGroupName + " uid="
+ uid + " packageName="
+ mPackageName + " category=" + category);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoCardViewPreference.java
index 018b0db41..68f3f0f42 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/BrowserRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoCardViewPreference.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,24 +14,20 @@
* limitations under the License.
*/
-package com.android.permissioncontroller.role.ui.behavior;
+package com.android.permissioncontroller.permission.ui.auto;
import android.content.Context;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
+import com.android.car.ui.preference.CarUiPreference;
import com.android.permissioncontroller.R;
-import com.android.role.controller.model.Role;
-/***
- * Class for UI behavior of Browser role
+/**
+ * A Preference representing a banner message represented as a CardView
*/
-public class BrowserRoleUiBehavior implements RoleUiBehavior {
+public class AutoCardViewPreference extends CarUiPreference {
- @Override
- public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return context.getResources().getBoolean(R.bool.config_showBrowserRole);
+ public AutoCardViewPreference(Context context) {
+ super(context);
+ this.setLayoutResource(R.layout.car_warning_banner_preference_card);
}
}
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..2350a5d71 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/AutoReviewPermissionDecisionsFragment.kt
@@ -35,6 +35,7 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERM
import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED
import com.android.permissioncontroller.R
import com.android.permissioncontroller.auto.AutoSettingsFrameFragment
+import com.android.permissioncontroller.auto.DrivingDecisionReminderService
import com.android.permissioncontroller.permission.data.v33.PermissionDecision
import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModel
@@ -53,20 +54,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 +95,24 @@ 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)
+ ) {
+ DrivingDecisionReminderService.cancelNotification(requireActivity())
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 +141,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 +187,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 +247,8 @@ class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() {
sessionId,
RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED,
0,
- null)
+ null
+ )
}
private fun logViewAllClicked() {
@@ -233,7 +257,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 +268,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/GrantPermissionsAutoViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
index 6b09921cb..9f2e54b2f 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -102,7 +102,7 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand
mGroupIcon = icon;
mGroupMessage = message;
mDetailMessage = detailMessage;
- mButtonVisibilities = buttonVisibilities;
+ setButtonVisibilities(buttonVisibilities);
update();
}
@@ -116,7 +116,6 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand
AlertDialogBuilder builder = new AlertDialogBuilder(mContext)
.setTitle(mGroupMessage)
- .setSubtitle(mDetailMessage)
.setAllowDismissButton(false)
.setOnDismissListener((dialog) -> {
mDialog = null;
@@ -128,6 +127,17 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand
List<CarUiListItem> itemList = new ArrayList<>();
+
+ // TODO(b/343727055): We are adding the subtitle to the item list so that it is
+ // scrollable. When the title and the subtitle are long, the buttons in the
+ // AlertDialog are not visible to the user.
+ if (mDetailMessage != null && !(mDetailMessage.length() == 0)) {
+ CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.NONE);
+ item.setSecure(true);
+ item.setTitle(mDetailMessage);
+ itemList.add(item);
+ }
+
// Don't show the allow one time button as per automotive design decisions
createListItem(itemList, R.string.grant_dialog_button_allow,
GRANTED_ALWAYS, ALLOW_BUTTON);
@@ -193,11 +203,19 @@ public class GrantPermissionsAutoViewHandler implements GrantPermissionsViewHand
mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT);
mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX);
mDetailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE);
- mButtonVisibilities = savedInstanceState.getBooleanArray(ARG_BUTTON_VISIBILITIES);
+ setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_BUTTON_VISIBILITIES));
update();
}
+ private void setButtonVisibilities(boolean[] visibilities) {
+ // If GrantPermissionsActivity sent the user directly to settings, button visibilities are
+ // not created. If the activity was then destroyed by the system, once the activity is
+ // recreated to perform onActivityResult, it will try to loadInstanceState in onCreate but
+ // the button visibilities were never set, so they will be null.
+ mButtonVisibilities = visibilities == null ? new boolean[0] : visibilities;
+ }
+
@Override
public void onBackPressed() {
if (mDialog != null) {
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..d00d0b126 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/AppPermissionFragment.java
@@ -52,12 +52,14 @@ import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.text.BidiFormatter;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.Switch;
@@ -83,6 +85,7 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod
import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.widget.ActionBarShadowController;
@@ -103,20 +106,26 @@ 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";
+ static final String PERSISTENT_DEVICE_ID = "persistent_device_id";
private @NonNull AppPermissionViewModel mViewModel;
private @NonNull ViewGroup mAppPermissionRationaleContainer;
private @NonNull ViewGroup mAppPermissionRationaleContent;
+ private @NonNull FrameLayout mAllowButtonFrame;
private @NonNull RadioButton mAllowButton;
private @NonNull RadioButton mAllowAlwaysButton;
private @NonNull RadioButton mAllowForegroundButton;
private @NonNull RadioButton mAskOneTimeButton;
private @NonNull RadioButton mAskButton;
- private @NonNull RadioButton mSelectPhotosButton;
+ private @NonNull RadioButton mSelectButton;
private @NonNull RadioButton mDenyButton;
private @NonNull RadioButton mDenyForegroundButton;
+ private @NonNull ImageView mEditSelectedPhotosButton;
+ private @NonNull View mAllowLimitedPhotosLayout;
+ private @NonNull View mSelectPhotosDivider;
private @NonNull View mLocationAccuracy;
private @NonNull Switch mLocationAccuracySwitch;
private @NonNull View mDivider;
@@ -128,7 +137,10 @@ 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 String mPersistentDeviceId;
private @NonNull String mPackageLabel;
private @NonNull String mPermGroupLabel;
@@ -192,8 +204,12 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
mPackageName, mUser);
mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
+ mPersistentDeviceId = getArguments().getString(PERSISTENT_DEVICE_ID,
+ MultiDeviceUtils.getDefaultDevicePersistentDeviceId());
+
AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory(
- getActivity().getApplication(), mPackageName, mPermGroupName, mUser, mSessionId);
+ getActivity().getApplication(), mPackageName, mPermGroupName, mUser, mSessionId,
+ mPersistentDeviceId);
mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class);
Handler delayHandler = new Handler(Looper.getMainLooper());
mViewModel.getButtonStateLiveData().observe(this, buttonState -> {
@@ -209,7 +225,6 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
if (mIsStorageGroup) {
mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState);
}
- mViewModel.registerPhotoPickerResultIfNeeded(this);
mRoleManager = Utils.getSystemServiceSafe(getContext(), RoleManager.class);
}
@@ -225,8 +240,15 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
setHeader(mPackageIcon, mPackageLabel, null, null, false);
updateHeader(root.requireViewById(R.id.large_header));
- ((TextView) root.requireViewById(R.id.permission_message)).setText(
- context.getString(R.string.app_permission_header, mPermGroupLabel));
+ String text = null;
+ if (MultiDeviceUtils.isDefaultDeviceId(mPersistentDeviceId)) {
+ text = context.getString(R.string.app_permission_header, mPermGroupLabel);
+ } else {
+ final String deviceName = MultiDeviceUtils.getDeviceName(context, mPersistentDeviceId);
+ text = context.getString(R.string.app_permission_header_with_device_name,
+ mPermGroupLabel, deviceName);
+ }
+ ((TextView) root.requireViewById(R.id.permission_message)).setText(text);
String caller = getArguments().getString(EXTRA_CALLER_NAME);
@@ -253,20 +275,24 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
footerInfoText.setVisibility(View.GONE);
}
+ mAllowButtonFrame = root.requireViewById(R.id.allow_radio_button_frame);
mAllowButton = root.requireViewById(R.id.allow_radio_button);
mAllowAlwaysButton = root.requireViewById(R.id.allow_always_radio_button);
mAllowForegroundButton = root.requireViewById(R.id.allow_foreground_only_radio_button);
mAskOneTimeButton = root.requireViewById(R.id.ask_one_time_radio_button);
mAskButton = root.requireViewById(R.id.ask_radio_button);
- mSelectPhotosButton = root.requireViewById(R.id.select_radio_button);
+ mSelectButton = 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);
-
+ mAllowLimitedPhotosLayout = root.requireViewById(R.id.radio_select_layout);
+ mEditSelectedPhotosButton = root.requireViewById(R.id.edit_selected_button);
+ mSelectPhotosDivider = root.requireViewById(R.id.edit_photos_divider);
mNestedScrollView = root.requireViewById(R.id.nested_scroll_view);
if (mViewModel.getButtonStateLiveData().getValue() != null) {
@@ -280,6 +306,9 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
mDenyButton.setVisibility(View.GONE);
mDenyForegroundButton.setVisibility(View.GONE);
mLocationAccuracy.setVisibility(View.GONE);
+ mAllowLimitedPhotosLayout.setVisibility(View.GONE);
+ mSelectPhotosDivider.setAlpha(0f);
+ mEditSelectedPhotosButton.setAlpha(0f);
}
if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) {
@@ -302,6 +331,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);
@@ -372,15 +407,11 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
} else if (states == null) {
return;
}
-
- mAllowButton.setOnClickListener((v) -> {
- mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND,
- APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW);
- setResult(GRANTED_ALWAYS);
- });
+ mAllowButtonFrame.setOnClickListener((v) -> allowButtonFrameClickListener());
mAllowAlwaysButton.setOnClickListener((v) -> {
+ markSingleButtonAsChecked(ButtonType.ALLOW_ALWAYS);
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,
@@ -389,6 +420,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
setResult(GRANTED_ALWAYS);
});
mAllowForegroundButton.setOnClickListener((v) -> {
+ markSingleButtonAsChecked(ButtonType.ALLOW_FOREGROUND);
if (mIsStorageGroup) {
mViewModel.setAllFilesAccess(false);
mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH,
@@ -400,19 +432,32 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
setResult(GRANTED_FOREGROUND_ONLY);
}
});
- // mAskOneTimeButton only shows if checked hence should do nothing
+ mAskOneTimeButton.setOnClickListener((v) -> {
+ // mAskOneTimeButton only shows if checked hence should do nothing
+ markSingleButtonAsChecked(ButtonType.ASK_ONCE);
+ });
mAskButton.setOnClickListener((v) -> {
+ markSingleButtonAsChecked(ButtonType.ASK);
mViewModel.requestChange(true, this, this, ChangeRequest.REVOKE_BOTH,
APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME);
setResult(DENIED);
});
- mSelectPhotosButton.setOnClickListener((v) -> {
+ mSelectButton.setOnClickListener((v) -> {
+ markSingleButtonAsChecked(ButtonType.SELECT_PHOTOS);
int buttonPressed =
APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__PHOTOS_SELECTED;
mViewModel.requestChange(false, this, this, ChangeRequest.PHOTOS_SELECTED,
buttonPressed);
});
+ mEditSelectedPhotosButton.setOnClickListener((v) -> {
+ ButtonState selectState = states.get(ButtonType.SELECT_PHOTOS);
+ if (selectState != null && selectState.isChecked() && !mPhotoPickerTriggered) {
+ mPhotoPickerTriggered = true;
+ mViewModel.openPhotoPicker(this);
+ }
+ });
mDenyButton.setOnClickListener((v) -> {
+ markSingleButtonAsChecked(ButtonType.DENY);
if (mViewModel.getFullStorageStateLiveData().getValue() != null
&& !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) {
mViewModel.setAllFilesAccess(false);
@@ -422,6 +467,7 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
setResult(DENIED_DO_NOT_ASK_AGAIN);
});
mDenyForegroundButton.setOnClickListener((v) -> {
+ markSingleButtonAsChecked(ButtonType.DENY_FOREGROUND);
mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_FOREGROUND,
APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND);
setResult(DENIED_DO_NOT_ASK_AGAIN);
@@ -449,8 +495,8 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
setButtonState(mAskButton, states.get(ButtonType.ASK));
setButtonState(mDenyButton, states.get(ButtonType.DENY));
setButtonState(mDenyForegroundButton, states.get(ButtonType.DENY_FOREGROUND));
- setButtonState(mSelectPhotosButton, states.get(ButtonType.SELECT_PHOTOS));
- if (mSelectPhotosButton.getVisibility() == View.VISIBLE) {
+ setButtonState(mSelectButton, states.get(ButtonType.SELECT_PHOTOS));
+ if (mSelectButton.getVisibility() == View.VISIBLE) {
mAllowButton.setText(R.string.app_permission_button_always_allow_all);
} else {
mAllowButton.setText(R.string.app_permission_button_allow);
@@ -475,6 +521,17 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
}
}
+ private void allowButtonFrameClickListener() {
+ if (!mAllowButton.isEnabled()) {
+ mViewModel.handleDisabledAllowButton(this);
+ } else {
+ markSingleButtonAsChecked(ButtonType.ALLOW);
+ mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW);
+ setResult(GRANTED_ALWAYS);
+ }
+ }
+
private void setButtonState(CompoundButton button, AppPermissionViewModel.ButtonState state) {
int visible = state.isShown() ? View.VISIBLE : View.GONE;
button.setVisibility(visible);
@@ -485,6 +542,21 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
if (mIsInitialLoad) {
button.jumpDrawablesToCurrentState();
}
+
+ if (button == mSelectButton) {
+ mAllowLimitedPhotosLayout.setVisibility(visible);
+ float endOpacity = state.isChecked() ? 1f : 0f;
+ // On initial load, do not show the fade in/out animation
+ if (mIsInitialLoad) {
+ mSelectPhotosDivider.setAlpha(endOpacity);
+ mEditSelectedPhotosButton.setAlpha(endOpacity);
+ return;
+ }
+ mEditSelectedPhotosButton.animate().alpha(endOpacity)
+ .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS);
+ mSelectPhotosDivider.animate().alpha(endOpacity)
+ .setDuration(EDIT_PHOTOS_BUTTON_ANIMATION_LENGTH_MS);
+ }
}
private void setSpecialStorageState(FullStoragePackageState storageState, View v) {
@@ -608,6 +680,24 @@ public class AppPermissionFragment extends SettingsWithLargeHeader
ConfirmDialog.class.getName());
}
+ private void markSingleButtonAsChecked(ButtonType buttonToMarkChecked) {
+ if (!mViewModel.getButtonStateLiveData().isInitialized()) {
+ return;
+ }
+ Map<ButtonType, ButtonState> currentStates = mViewModel.getButtonStateLiveData().getValue();
+ Map<ButtonType, ButtonState> newStates = new ArrayMap<>();
+ for (ButtonType button: currentStates.keySet()) {
+ ButtonState state = currentStates.get(button);
+ boolean isChecked = button == buttonToMarkChecked
+ // Don't uncheck the Location Accuracy switch, if it is checked
+ || (button == ButtonType.LOCATION_ACCURACY && state.isChecked());
+ ButtonState newState = new ButtonState(isChecked, state.isEnabled(), state.isShown(),
+ state.getCustomRequest());
+ newStates.put(button, newState);
+ }
+ setRadioButtonsState(newStates);
+ }
+
/**
* A dialog warning the user that they are about to deny a permission that was granted by
* default, or that they are denying a permission on a Pre-M app
@@ -630,7 +720,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..eff5738fc 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;
@@ -63,6 +64,7 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.flags.Flags;
import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState;
@@ -75,6 +77,7 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsV
import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.StringUtils;
import com.android.permissioncontroller.permission.utils.Utils;
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.widget.FooterPreference;
@@ -318,6 +321,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()) {
@@ -345,7 +351,19 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
PermissionControlPreference preference = new PermissionControlPreference(context,
mPackageName, groupName, mUser, AppPermissionGroupsFragment.class.getName(),
sessionId, grantCategory.getCategoryName(), true);
- preference.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(context, groupName));
+
+ CharSequence permissionGroupName = KotlinUtils.INSTANCE.getPermGroupLabel(context,
+ groupName);
+ if (MultiDeviceUtils.isDefaultDeviceId(groupInfo.getPersistentDeviceId())) {
+ preference.setTitle(permissionGroupName);
+ } else {
+ final String deviceName = MultiDeviceUtils.getDeviceName(context,
+ groupInfo.getPersistentDeviceId());
+ preference.setTitle(context.getString(
+ R.string.permission_group_name_with_device_name,
+ permissionGroupName, deviceName));
+ preference.setPersistentDeviceId(groupInfo.getPersistentDeviceId());
+ }
preference.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, groupName));
preference.setKey(groupName);
String summary = mViewModel.getPreferenceSummary(groupInfo, context,
@@ -427,8 +445,10 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
int switchTitleId;
if (isHibernationEnabled()) {
if (SdkLevel.isAtLeastT()) {
- switchTitleId = R.string.unused_apps_label_v2;
- autoRevokeSwitch.setSummary(R.string.unused_apps_summary);
+ switchTitleId = isArchivingEnabled() ? R.string.unused_apps_label_v3
+ : R.string.unused_apps_label_v2;
+ autoRevokeSwitch.setSummary(isArchivingEnabled() ? R.string.unused_apps_summary_v2
+ : R.string.unused_apps_summary);
} else {
switchTitleId = R.string.unused_apps_label;
}
@@ -452,6 +472,10 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
autoRevokeCategory.addPreference(autoRevokeSummary);
}
+ private boolean isArchivingEnabled() {
+ return SdkLevel.isAtLeastV() && Flags.archivingReadOnly();
+ }
+
private void setAutoRevokeToggleState(HibernationSettingState state) {
if (state == null || !mViewModel.getPackagePermGroupsLiveData().isInitialized()
|| getPreferenceScreen() == null || getListView() == null || getView() == null) {
@@ -580,7 +604,7 @@ public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader i
}
PermissionControllerStatsLog.write(APP_PERMISSIONS_FRAGMENT_VIEWED, sessionId, viewId,
permissionGroupName, uid, mPackageName, category);
- Log.v(LOG_TAG, "AppPermissionFragment view logged with sessionId=" + sessionId + " viewId="
+ Log.i(LOG_TAG, "AppPermissionFragment view logged with sessionId=" + sessionId + " viewId="
+ viewId + " permissionGroupName=" + permissionGroupName + " uid="
+ uid + " packageName="
+ mPackageName + " category=" + category);
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..bb698b49a 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()
@@ -159,7 +166,6 @@ class GrantPermissionsViewHandlerImpl(
buttonVisibilities: BooleanArray?,
locationVisibilities: BooleanArray?
) {
-
this.groupName = groupName
this.groupCount = groupCount
this.groupIndex = groupIndex
@@ -187,21 +193,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 +264,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 +290,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 +339,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 +384,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 +408,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 +430,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 +449,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 +509,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 +519,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 +647,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/ManageStandardPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
index 8e3192eee..86fcf2c27 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ManageStandardPermissionsFragment.java
@@ -22,11 +22,8 @@ import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack;
import android.app.Application;
-import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.lifecycle.ViewModelProvider;
@@ -35,10 +32,8 @@ import androidx.preference.PreferenceScreen;
import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
import com.android.permissioncontroller.permission.ui.UnusedAppsFragment;
import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel;
-import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.permissioncontroller.permission.utils.StringUtils;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.settingslib.widget.FooterPreference;
@@ -50,9 +45,6 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
private static final String EXTRA_PREFS_KEY = "extra_prefs_key";
private static final String AUTO_REVOKE_KEY = "auto_revoke_key";
private static final String LOG_TAG = ManageStandardPermissionsFragment.class.getSimpleName();
-
- private static final int MENU_PERMISSION_USAGE = MENU_HIDE_SYSTEM + 1;
-
private ManageStandardPermissionsViewModel mViewModel;
/**
@@ -111,24 +103,11 @@ public final class ManageStandardPermissionsFragment extends ManagePermissionsFr
case android.R.id.home:
pressBack(this);
return true;
- case MENU_PERMISSION_USAGE:
- getActivity().startActivity(new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
- .setClass(getContext(), ManagePermissionsActivity.class));
- return true;
}
return super.onOptionsItemSelected(item);
}
@Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
-
- if (KotlinUtils.INSTANCE.shouldShowPermissionsDashboard()) {
- menu.add(Menu.NONE, MENU_PERMISSION_USAGE, Menu.NONE, R.string.permission_usage_title);
- }
- }
-
- @Override
protected PreferenceScreen updatePermissionsUi() {
PreferenceScreen screen = super.updatePermissionsUi();
if (screen == null) {
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..76e52ad94 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;
@@ -33,7 +34,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
+import android.safetycenter.SafetyCenterManager;
import android.util.ArrayMap;
import android.view.Menu;
import android.view.MenuInflater;
@@ -52,7 +55,6 @@ import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage;
import com.android.permissioncontroller.permission.model.v31.PermissionUsages;
import com.android.permissioncontroller.permission.ui.Category;
-import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity;
import com.android.permissioncontroller.permission.ui.handheld.v31.CardViewPreference;
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel;
import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory;
@@ -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,11 +89,10 @@ 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
@@ -115,6 +116,7 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
private PermissionUsages mPermissionUsages;
private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>();
private Boolean mSensorStatus;
+ private UserManager mUserManager;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -168,6 +170,8 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus);
}
}
+
+ mUserManager = Utils.getSystemServiceSafe(getContext(), UserManager.class);
}
@Override
@@ -197,10 +201,6 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
updateMenu(mViewModel.getShouldShowSystemLiveData().getValue());
}
- if (KotlinUtils.INSTANCE.shouldShowPermissionsDashboard()) {
- menu.add(Menu.NONE, MENU_PERMISSION_USAGE, Menu.NONE, R.string.permission_usage_title);
- }
-
if (!SdkLevel.isAtLeastS()) {
HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_app_permissions,
getClass().getName());
@@ -218,11 +218,6 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
case MENU_HIDE_SYSTEM:
mViewModel.updateShowSystem(item.getItemId() == MENU_SHOW_SYSTEM);
break;
- case MENU_PERMISSION_USAGE:
- getActivity().startActivity(new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
- .setClass(getContext(), ManagePermissionsActivity.class)
- .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, mPermGroupName));
- return true;
}
return super.onOptionsItemSelected(item);
}
@@ -281,12 +276,31 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
findPreference(ALLOWED_FOREGROUND.getCategoryName()).setVisible(false);
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private String getPrivacyControlsIntent() {
+ Context context = getPreferenceManager().getContext();
+ SafetyCenterManager safetyCenterManager =
+ context.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 +317,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) {
@@ -426,6 +440,9 @@ public final class PermissionAppsFragment extends SettingsWithLargeHeader implem
}
for (Pair<String, UserHandle> packageUserLabel : packages) {
+ if (!Utils.shouldShowInSettings(packageUserLabel.getSecond(), mUserManager)) {
+ continue;
+ }
String packageName = packageUserLabel.getFirst();
UserHandle user = packageUserLabel.getSecond();
@@ -502,6 +519,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/PermissionControlPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java
index 318ebea06..039aca39d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionControlPreference.java
@@ -21,6 +21,7 @@ import static android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME;
import static com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment.GRANT_CATEGORY;
+import static com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment.PERSISTENT_DEVICE_ID;
import static com.android.permissioncontroller.permission.utils.KotlinUtilsKt.navigateSafe;
import android.Manifest;
@@ -69,6 +70,7 @@ public class PermissionControlPreference extends Preference {
private @NonNull long mSessionId;
private boolean mHasNavGraph;
private @NonNull UserHandle mUser;
+ private @Nullable String mPersistentDeviceId;
public PermissionControlPreference(@NonNull Context context,
@NonNull AppPermissionGroup group, @NonNull String caller) {
@@ -177,9 +179,11 @@ public class PermissionControlPreference extends Preference {
if (mUseSmallerIcon) {
ImageView icon = ((ImageView) holder.findViewById(android.R.id.icon));
icon.setMaxWidth(
- mContext.getResources().getDimensionPixelSize(R.dimen.secondary_app_icon_size));
+ mContext.getResources().getDimensionPixelSize(
+ com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size));
icon.setMaxHeight(
- mContext.getResources().getDimensionPixelSize(R.dimen.secondary_app_icon_size));
+ mContext.getResources().getDimensionPixelSize(
+ com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size));
}
super.onBindViewHolder(holder);
@@ -235,6 +239,7 @@ public class PermissionControlPreference extends Preference {
args.putString(EXTRA_CALLER_NAME, mCaller);
args.putLong(EXTRA_SESSION_ID, mSessionId);
args.putString(GRANT_CATEGORY, mGranted);
+ args.putString(PERSISTENT_DEVICE_ID, mPersistentDeviceId);
navigateSafe(Navigation.findNavController(holder.itemView), R.id.perm_groups_to_app,
args);
} else {
@@ -252,6 +257,10 @@ public class PermissionControlPreference extends Preference {
});
}
+ public void setPersistentDeviceId(String persistentDeviceId) {
+ this.mPersistentDeviceId = persistentDeviceId;
+ }
+
private void setIcons(PreferenceViewHolder holder, @Nullable List<Integer> icons, int frameId) {
ViewGroup frame = (ViewGroup) holder.findViewById(frameId);
if (icons != null && !icons.isEmpty()) {
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java
index 658a82af5..1089fcac2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionPreference.java
@@ -162,7 +162,8 @@ class PermissionPreference extends MultiTargetSwitchPreference {
if (mViewModel.isFixedOrForegroundDisabled(mGroup)) {
if (admin != null) {
- setWidgetLayoutResource(R.layout.restricted_icon);
+ setWidgetLayoutResource(
+ com.android.settingslib.widget.restricted.R.layout.restricted_icon);
setOnPreferenceClickListener((v) -> {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin);
@@ -303,9 +304,9 @@ class PermissionPreference extends MultiTargetSwitchPreference {
int getResource(SummaryMessage summary) {
switch (summary) {
case DISABLED_BY_ADMIN:
- return R.string.disabled_by_admin;
+ return com.android.settingslib.widget.restricted.R.string.disabled_by_admin;
case ENABLED_BY_ADMIN:
- return R.string.enabled_by_admin;
+ return com.android.settingslib.widget.restricted.R.string.enabled_by_admin;
case ENABLED_SYSTEM_FIXED:
return R.string.permission_summary_enabled_system_fixed;
case ENFORCED_BY_POLICY:
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java
index 2e9a99bb9..fde134b4d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsCollapsingToolbarBaseFragment.java
@@ -23,7 +23,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceFragmentCompat;
-import com.android.permissioncontroller.R;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseFragment;
/**
@@ -43,13 +42,15 @@ public abstract class PermissionsCollapsingToolbarBaseFragment
PreferenceFragmentCompat preferenceFragment =
(PreferenceFragmentCompat) getChildFragmentManager()
- .findFragmentById(R.id.content_frame);
+ .findFragmentById(
+ com.android.settingslib.collapsingtoolbar.R.id.content_frame);
if (preferenceFragment == null) {
preferenceFragment = createPreferenceFragment();
preferenceFragment.setArguments(getArguments());
getChildFragmentManager().beginTransaction()
- .add(R.id.content_frame, preferenceFragment)
+ .add(com.android.settingslib.collapsingtoolbar.R.id.content_frame,
+ preferenceFragment)
.commit();
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java
index ce7c5cfb9..17e72b413 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/PermissionsFrameFragment.java
@@ -87,8 +87,10 @@ public abstract class PermissionsFrameFragment extends PreferenceFragmentCompat
inflater, mPrefsView, savedInstanceState);
setLoading(mIsLoading, false, true /* force */);
mPrefsView.addView(mPreferencesContainer);
- mProgressHeader = rootView.requireViewById(R.id.progress_bar_animation);
- mProgressView = rootView.requireViewById(R.id.progress_bar_background);
+ mProgressHeader = rootView.requireViewById(
+ com.android.settingslib.widget.progressbar.R.id.progress_bar_animation);
+ mProgressView = rootView.requireViewById(
+ com.android.settingslib.widget.progressbar.R.id.progress_bar_background);
setProgressBarVisible(false);
getListView().setFocusable(false);
return rootView;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java
index 5e5c221ae..5a7c3f2b5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/ReviewPermissionsFragment.java
@@ -23,6 +23,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.REVI
import android.app.Activity;
import android.app.Application;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -45,6 +46,9 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -150,6 +154,12 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat
ViewGroup preferenceRootView = mView.requireViewById(R.id.preferences_frame);
View prefsContainer = super.onCreateView(inflater, preferenceRootView, savedInstanceState);
preferenceRootView.addView(prefsContainer);
+ ViewCompat.setOnApplyWindowInsetsListener(mView, (v, windowInsets) -> {
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
+ mView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsetsCompat.CONSUMED;
+ });
+
return mView;
}
@@ -258,7 +268,7 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat
changeId, mViewModel.getPackageInfo().applicationInfo.uid,
group.getPackageName(),
permission.getName(), permission.isGrantedIncludingAppOp());
- Log.v(LOG_TAG, "Permission grant via permission review changeId=" + changeId + " uid="
+ Log.i(LOG_TAG, "Permission grant via permission review changeId=" + changeId + " uid="
+ mViewModel.getPackageInfo().applicationInfo.uid + " packageName="
+ group.getPackageName() + " permission="
+ permission.getName() + " granted=" + permission.isGrantedIncludingAppOp());
@@ -396,7 +406,7 @@ public final class ReviewPermissionsFragment extends PreferenceFragmentCompat
}
activity.startIntentSenderForResult(intent, -1, null,
flagMask, flagValues, 0);
- } catch (IntentSender.SendIntentException e) {
+ } catch (IntentSender.SendIntentException | ActivityNotFoundException e) {
/* ignore */
}
return;
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java
index cbd4a0ce0..8745ac8f7 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/SettingsWithLargeHeader.java
@@ -107,7 +107,8 @@ public abstract class SettingsWithLargeHeader extends PermissionsFrameFragment
if (header != null) {
header.setVisibility(View.VISIBLE);
- ImageView appIcon = header.requireViewById(R.id.entity_header_icon);
+ ImageView appIcon = header.requireViewById(
+ com.android.settingslib.widget.preference.layout.R.id.entity_header_icon);
appIcon.setImageDrawable(mIcon);
if (mSmallIcon) {
int size = getContext().getResources().getDimensionPixelSize(
@@ -121,11 +122,18 @@ public abstract class SettingsWithLargeHeader extends PermissionsFrameFragment
appIcon.setContentDescription(mLabel);
}
- TextView appName = header.requireViewById(R.id.entity_header_title);
+ TextView appName = header.requireViewById(
+ com.android.settingslib.widget.preference.layout.R.id.entity_header_title);
appName.setText(mLabel);
- header.requireViewById(R.id.entity_header_summary).setVisibility(View.GONE);
- header.requireViewById(R.id.entity_header_second_summary).setVisibility(View.GONE);
+
+ header.requireViewById(
+ com.android.settingslib.widget.preference.layout.R.id.entity_header_summary)
+ .setVisibility(View.GONE);
+
+ header.requireViewById(
+ com.android.settingslib.widget.preference.layout.R.id.entity_header_second_summary)
+ .setVisibility(View.GONE);
header.requireViewById(R.id.header_link).setVisibility(View.GONE);
}
}
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..b31b3e484 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,
@@ -62,9 +63,13 @@ open class SmartIconLoadPackagePermissionPreference constructor(
val imageView = holder.findViewById(android.R.id.icon) as ImageView
imageView.maxWidth =
- context.resources.getDimensionPixelSize(R.dimen.secondary_app_icon_size)
+ context.resources.getDimensionPixelSize(
+ com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size
+ )
imageView.maxHeight =
- context.resources.getDimensionPixelSize(R.dimen.secondary_app_icon_size)
+ context.resources.getDimensionPixelSize(
+ com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size
+ )
imageView.setImageDrawable(KotlinUtils.getBadgedPackageIcon(app, packageName, user))
imageView.visibility = View.VISIBLE
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/CompositeCircleView.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/CompositeCircleView.java
index 64346c4aa..5965cfcf5 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/CompositeCircleView.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/CompositeCircleView.java
@@ -19,10 +19,13 @@ package com.android.permissioncontroller.permission.ui.handheld.v31;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.permissioncontroller.R;
+
/**
* Configured to draw a set of contiguous partial circles via {@link PartialCircleView}, which
* are generated from the relative weight of values and corresponding colors given to
@@ -73,8 +76,10 @@ public class CompositeCircleView extends FrameLayout {
* @param values relative weights, used to size the partial circles
* @param colors colors corresponding to relative weights
* @param strokeWidth stroke width to apply to all contained partial circles
+ * @param labels the permission labels to set the ContentDescription with % value
*/
- public void configure(float startAngle, int[] values, int[] colors, int strokeWidth) {
+ public void configure(float startAngle, int[] values, int[] colors, int strokeWidth,
+ TextView[] labels) {
removeAllViews();
mValues = values;
@@ -121,6 +126,13 @@ public class CompositeCircleView extends FrameLayout {
float sweepAngle = (values[i] / total) * allocatedDegrees;
pcv.setSweepAngle(sweepAngle);
+ if (labels[i] != null) {
+ int percentage = Math.round((values[i] / total) * 100);
+ String contextDescription = getContext().getString(
+ R.string.privdash_usage_percent, labels[i].getText(), percentage);
+ labels[i].setContentDescription(contextDescription);
+ }
+
mPartialCircleCenterAngles[i] = (startAngle + (sweepAngle * 0.5f)) % 360;
if (i > 0) {
float angleDiff =
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..c2b5b875b 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
@@ -20,6 +20,7 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.PERM
import static com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_DETAILS_INTERACTION__ACTION__INFO_ICON_CLICKED;
import static com.android.permissioncontroller.PermissionControllerStatsLog.write;
+import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -28,6 +29,7 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.text.format.DateFormat;
+import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -47,7 +49,7 @@ import com.android.permissioncontroller.permission.compat.IntentCompat;
import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel;
import com.android.permissioncontroller.permission.utils.Utils;
-import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -65,25 +67,24 @@ public class PermissionHistoryPreference extends Preference {
private final long mAccessEndTime;
private final Drawable mAppIcon;
private final String mTitle;
- private final ArrayList<String> mAttributionTags;
+ private final List<String> mAttributionTags;
private final boolean mIsLastUsage;
private final Intent mIntent;
private final boolean mShowingAttribution;
private final PackageManager mUserPackageManager;
+ private final PermissionUsageDetailsViewModel.PermissionUsageOnClickDialog mOnClickDialog;
private final long mSessionId;
private Drawable mWidgetIcon;
- public PermissionHistoryPreference(@NonNull Context context,
- @NonNull UserHandle userHandle, @NonNull String pkgName,
- @NonNull Drawable appIcon,
- @NonNull String preferenceTitle,
- @NonNull String permissionGroup,
- @NonNull long accessStartTime,
- @NonNull long accessEndTime,
- @Nullable CharSequence summaryText, boolean showingAttribution,
- @NonNull ArrayList<String> attributionTags, boolean isLastUsage, long sessionId) {
+ public PermissionHistoryPreference(@NonNull Context context, @NonNull UserHandle userHandle,
+ @NonNull String pkgName, @Nullable Drawable appIcon, @NonNull String preferenceTitle,
+ @NonNull String permissionGroup, @NonNull long accessStartTime,
+ @NonNull long accessEndTime, @Nullable CharSequence summaryText,
+ boolean showingAttribution, @NonNull List<String> attributionTags,
+ boolean isLastUsage, long sessionId,
+ @Nullable PermissionUsageDetailsViewModel.PermissionUsageOnClickDialog onClickDialog) {
super(context);
mContext = context;
Context userContext = Utils.getUserContext(context, userHandle);
@@ -100,6 +101,7 @@ public class PermissionHistoryPreference extends Preference {
mIsLastUsage = isLastUsage;
mSessionId = sessionId;
mShowingAttribution = showingAttribution;
+ mOnClickDialog = onClickDialog;
setTitle(mTitle);
if (summaryText != null) {
@@ -150,19 +152,39 @@ public class PermissionHistoryPreference extends Preference {
// It's temporarily created via a static method due to ongoing ViewModel refactoring.
Intent intent =
PermissionUsageDetailsViewModel.Companion.createHistoryPreferenceClickIntent(
- mContext,
- mUserHandle,
- mPackageName,
- mPermissionGroup,
- mAccessStartTime,
- mAccessEndTime,
- mShowingAttribution,
- mAttributionTags);
-
- setOnPreferenceClickListener((preference) -> {
- mContext.startActivityAsUser(intent, mUserHandle);
- return true;
- });
+ mContext,
+ mUserHandle,
+ mPackageName,
+ mPermissionGroup,
+ mAccessStartTime,
+ mAccessEndTime,
+ mShowingAttribution,
+ mAttributionTags);
+
+ if (mOnClickDialog != null) {
+ setOnPreferenceClickListener(preference -> {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext)
+ .setTitle(mOnClickDialog.getTitle())
+ .setMessage(mOnClickDialog.getDescription())
+ .setPositiveButton(R.string.app_permissions, (dialog, which) -> {
+ mContext.startActivityAsUser(intent, mUserHandle);
+ })
+ .setNegativeButton(R.string.dialog_close, null);
+
+ AlertDialog alertDialog = dialogBuilder.create();
+ alertDialog.show();
+ TextView messageView = alertDialog.findViewById(android.R.id.message);
+ if (messageView != null) {
+ messageView.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+ return true;
+ });
+ } else {
+ setOnPreferenceClickListener((preference) -> {
+ mContext.startActivityAsUser(intent, mUserHandle);
+ return true;
+ });
+ }
}
private void setInfoIcon(@NonNull PreferenceViewHolder holder, ImageView widgetView) {
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..0740a08fd 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
@@ -359,7 +359,7 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader {
category.get().setTitle(R.string.permission_history_category_yesterday);
} else {
category.get()
- .setTitle(DateFormat.getDateFormat(context).format(currentDateMs));
+ .setTitle(DateFormat.getLongDateFormat(context).format(currentDateMs));
}
previousDateMs = currentDateMs;
}
@@ -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(),
@@ -384,7 +378,8 @@ public class PermissionUsageDetailsFragment extends SettingsWithLargeHeader {
appPermissionAccessUiInfo.getShowingAttribution(),
appPermissionAccessUiInfo.getAttributionTags(),
i == appPermissionAccessUiInfoList.size() - 1,
- mSessionId);
+ mSessionId,
+ appPermissionAccessUiInfo.getOnClickDialog());
category.get().addPreference(permissionUsagePreference);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java
index e28387f5b..7102fae9a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageFragment.java
@@ -28,6 +28,7 @@ import android.app.ActionBar;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -42,7 +43,9 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.ui.handheld.SettingsWithLargeHeader;
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel;
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel;
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModelFactory;
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState;
import com.android.permissioncontroller.permission.utils.KotlinUtils;
import com.android.settingslib.HelpUtils;
@@ -54,7 +57,7 @@ import java.util.Map;
/** The main page for the privacy dashboard. */
@RequiresApi(Build.VERSION_CODES.S)
public class PermissionUsageFragment extends SettingsWithLargeHeader {
-
+ private static final String LOG_TAG = PermissionUsageFragment.class.getSimpleName();
private static final Map<String, Integer> PERMISSION_GROUP_ORDER =
Map.of(
Manifest.permission_group.LOCATION, 0,
@@ -62,6 +65,8 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
Manifest.permission_group.MICROPHONE, 2);
private static final int DEFAULT_ORDER = 3;
+ public static final boolean DEBUG = true;
+
// Pie chart in this screen will be the first child.
// Hence we use PERMISSION_GROUP_ORDER + 1 here.
private static final int PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT =
@@ -93,17 +98,16 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getLong(SESSION_ID_KEY);
} else {
mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
}
- PermissionUsageViewModel.PermissionUsageViewModelFactory factory =
- new PermissionUsageViewModel.PermissionUsageViewModelFactory(
+ PermissionUsageViewModelFactory factory = new PermissionUsageViewModelFactory(
getActivity().getApplication(), this, new Bundle());
- mViewModel = new ViewModelProvider(this, factory).get(PermissionUsageViewModel.class);
+ mViewModel = new ViewModelProvider(this, factory)
+ .get(PermissionUsageViewModel.class);
// Start out with 'other' permissions not expanded.
mOtherExpanded = false;
@@ -194,11 +198,7 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE, R.string.menu_show_system);
mHideSystemMenu =
menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE, R.string.menu_hide_system);
- boolean showSystem =
- mViewModel.getShowSystemAppsLiveData().getValue() != null
- ? mViewModel.getShowSystemAppsLiveData().getValue()
- : false;
- updateShowSystemToggle(showSystem);
+ updateShowSystemToggle(mViewModel.getShowSystemApps());
if (KotlinUtils.INSTANCE.is7DayToggleEnabled()) {
mShow7DaysDataMenu =
@@ -213,11 +213,7 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
MENU_SHOW_24_HOURS_DATA,
Menu.NONE,
R.string.menu_show_24_hours_data);
- boolean show7Days =
- mViewModel.getShow7DaysLiveData().getValue() != null
- ? mViewModel.getShow7DaysLiveData().getValue()
- : false;
- updateShow7DaysToggle(show7Days);
+ updateShow7DaysToggle(mViewModel.getShow7DaysData());
}
HelpUtils.prepareHelpMenuItem(
@@ -236,16 +232,16 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
PERMISSION_USAGE_FRAGMENT_INTERACTION,
mSessionId,
PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SHOW_SYSTEM_CLICKED);
- mViewModel.updateShowSystem(true);
+ updateAllUI(mViewModel.updateShowSystem(true));
break;
case MENU_HIDE_SYSTEM:
- mViewModel.updateShowSystem(false);
+ updateAllUI(mViewModel.updateShowSystem(false));
break;
case MENU_SHOW_7_DAYS_DATA:
- mViewModel.updateShow7Days(KotlinUtils.INSTANCE.is7DayToggleEnabled());
+ updateAllUI(mViewModel.updateShow7Days(KotlinUtils.INSTANCE.is7DayToggleEnabled()));
break;
case MENU_SHOW_24_HOURS_DATA:
- mViewModel.updateShow7Days(false);
+ updateAllUI(mViewModel.updateShow7Days(false));
break;
}
return super.onOptionsItemSelected(item);
@@ -293,11 +289,14 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
}
/** Updates page content and menu items. */
- private void updateAllUI(
- PermissionUsageViewModel.PermissionUsagesUiData permissionUsagesUiData) {
- if (getActivity() == null) {
+ private void updateAllUI(PermissionUsagesUiState uiData) {
+ Log.v(LOG_TAG, "Privacy dashboard data = " + uiData);
+ if (getActivity() == null || uiData instanceof PermissionUsagesUiState.Loading) {
return;
}
+
+ PermissionUsagesUiState.Success permissionUsagesUiData =
+ (PermissionUsagesUiState.Success) uiData;
Context context = getActivity();
PreferenceScreen screen = getPreferenceScreen();
@@ -319,40 +318,27 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
mSessionId,
PERMISSION_USAGE_FRAGMENT_INTERACTION__ACTION__SEE_OTHER_PERMISSIONS_CLICKED);
});
- boolean containsSystemAppUsages = permissionUsagesUiData.getContainsSystemAppUsages();
+ boolean containsSystemAppUsages = permissionUsagesUiData.getShouldShowSystemToggle();
Map<String, Integer> permissionGroupWithUsageCounts =
- permissionUsagesUiData.getPermissionGroupsWithUsageCount();
+ permissionUsagesUiData.getPermissionGroupUsageCount();
List<Map.Entry<String, Integer>> permissionGroupWithUsageCountsEntries =
new ArrayList(permissionGroupWithUsageCounts.entrySet());
permissionGroupWithUsageCountsEntries.sort(Comparator.comparing(
(Map.Entry<String, Integer> permissionGroupWithUsageCount) ->
PERMISSION_GROUP_ORDER.getOrDefault(
- permissionGroupWithUsageCount.getKey(),
- DEFAULT_ORDER))
- .thenComparing(
- (Map.Entry<String, Integer> permissionGroupWithUsageCount) ->
- KotlinUtils.INSTANCE
- .getPermGroupLabel(
- context,
- permissionGroupWithUsageCount
- .getKey())
- .toString()));
+ permissionGroupWithUsageCount.getKey(), DEFAULT_ORDER))
+ .thenComparing((Map.Entry<String, Integer> permissionGroupWithUsageCount) ->
+ mViewModel.getPermissionGroupLabel(
+ context, permissionGroupWithUsageCount.getKey())));
if (mHasSystemApps != containsSystemAppUsages) {
mHasSystemApps = containsSystemAppUsages;
}
- boolean show7Days =
- mViewModel.getShow7DaysLiveData().getValue() != null
- ? mViewModel.getShow7DaysLiveData().getValue()
- : false;
+ boolean show7Days = mViewModel.getShow7DaysData();
updateShow7DaysToggle(show7Days);
- boolean showSystem =
- mViewModel.getShowSystemAppsLiveData().getValue() != null
- ? mViewModel.getShowSystemAppsLiveData().getValue()
- : false;
- updateShowSystemToggle(showSystem);
+ updateShowSystemToggle(mViewModel.getShowSystemApps());
mGraphic = new PermissionUsageGraphicPreference(context, show7Days);
screen.addPreference(mGraphic);
@@ -381,7 +367,7 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
permissionGroupWithUsageCounts
.get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT - 1)
.getKey();
- return KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName);
+ return mViewModel.getPermissionGroupLabel(context, permGroupName);
}
String permGroupName1 =
@@ -392,10 +378,8 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
permissionGroupWithUsageCounts
.get(PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT)
.getKey();
- CharSequence permGroupLabel1 =
- KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName1);
- CharSequence permGroupLabel2 =
- KotlinUtils.INSTANCE.getPermGroupLabel(context, permGroupName2);
+ CharSequence permGroupLabel1 = mViewModel.getPermissionGroupLabel(context, permGroupName1);
+ CharSequence permGroupLabel2 = mViewModel.getPermissionGroupLabel(context, permGroupName2);
// case for 2 extra items in the advanced info
if (size == PERMISSION_USAGE_INITIAL_EXPANDED_CHILDREN_COUNT + 1) {
@@ -421,14 +405,9 @@ public class PermissionUsageFragment extends SettingsWithLargeHeader {
Context context,
List<Map.Entry<String, Integer>> permissionGroupWithUsageCounts,
PreferenceCategory category) {
- boolean showSystem =
- mViewModel.getShowSystemAppsLiveData().getValue() != null
- ? mViewModel.getShowSystemAppsLiveData().getValue()
- : false;
- boolean show7Days =
- mViewModel.getShow7DaysLiveData().getValue() != null
- ? mViewModel.getShow7DaysLiveData().getValue()
- : false;
+ boolean showSystem = mViewModel.getShowSystemApps();
+ boolean show7Days = mViewModel.getShow7DaysData();
+
for (int i = 0; i < permissionGroupWithUsageCounts.size(); i++) {
Map.Entry<String, Integer> permissionGroupWithUsageCount =
permissionGroupWithUsageCounts.get(i);
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageGraphicPreference.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageGraphicPreference.java
index d0a98d9e9..39bb4ed2b 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageGraphicPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/handheld/v31/PermissionUsageGraphicPreference.java
@@ -197,7 +197,7 @@ public class PermissionUsageGraphicPreference extends Preference {
// Configure circle and labeler.
ccvl.configure(R.id.composite_circle_view, centerLabel, labels, labelRadiusScalar);
// Start at angle 300 (top right) to allow for small segments for cam, mic, and loc.
- ccv.configure(300, counts, colors, circleStrokeWidth);
+ ccv.configure(300, counts, colors, circleStrokeWidth, labels);
}
private int getUsageCount(String group) {
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..3ef11a4d7 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
@@ -22,10 +22,10 @@ import android.text.style.ClickableSpan
import android.util.AttributeSet
import android.view.View
import android.widget.TextView
+import androidx.core.text.method.LinkMovementMethodCompat
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.compat.LinkMovementMethodCompat
/** A preference for a footer with an icon and a link. */
class AppDataSharingUpdatesFooterPreference : Preference {
@@ -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..1da058141 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
}
}
@@ -282,7 +287,7 @@ class AppDataSharingUpdatesFragment : PermissionsFrameFragment() {
sessionId,
numberOfAppUpdates
)
- Log.v(
+ Log.i(
LOG_TAG,
"AppDataSharingUpdatesFragment viewed with" +
" sessionId=$sessionId" +
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..9eb7a0fa4 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
@@ -33,8 +33,8 @@ import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.RequiresApi
+import androidx.core.text.method.LinkMovementMethodCompat
import com.android.permissioncontroller.R
-import com.android.permissioncontroller.permission.compat.LinkMovementMethodCompat
import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler
import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleViewHandler.Result.Companion.CANCELLED
@@ -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..c782a85f8 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
+ )
}
/**
@@ -174,15 +183,17 @@ class PermissionUsageDetailsViewModelLegacy(
private fun extractAppPermissionTimelineUsagesForGroup(
appPermissionUsages: List<AppPermissionUsage>,
group: String
- ): List<AppPermissionTimelineUsage> =
- appPermissionUsages
- .filter { !Utils.getExemptedPackages(roleManager).contains(it.packageName) }
+ ): List<AppPermissionTimelineUsage> {
+ val exemptedPackages = Utils.getExemptedPackages(roleManager)
+ return appPermissionUsages.filter { !exemptedPackages.contains(it.packageName) }
.map { appPermissionUsage ->
getAppPermissionTimelineUsages(
appPermissionUsage.app,
- appPermissionUsage.groupUsages.firstOrNull { it.group.name == group })
+ appPermissionUsage.groupUsages.firstOrNull { it.group.name == group }
+ )
}
.flatten()
+ }
/** Returns whether the show/hide system toggle should be displayed in the UI. */
private fun shouldDisplayShowSystemToggle(
@@ -221,7 +232,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 +249,9 @@ class PermissionUsageDetailsViewModelLegacy(
.sortedWith(
compareBy(
{ -it.discreteAccessDataList.first().accessTimeMs },
- { it.appPermissionTimelineUsage.permissionApp.label }))
+ { it.appPermissionTimelineUsage.permissionApp.label }
+ )
+ )
.toList()
/**
@@ -253,11 +269,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 +286,8 @@ class PermissionUsageDetailsViewModelLegacy(
}
if (currentDiscreteAccessDataList.isNotEmpty()) {
clusterDataList.add(
- DiscreteAccessClusterData(
- appPermissionTimelineUsage, currentDiscreteAccessDataList))
+ DiscreteAccessClusterData(appPermissionTimelineUsage, currentDiscreteAccessDataList)
+ )
}
return clusterDataList
}
@@ -282,8 +302,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 +398,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 +428,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 +460,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 +475,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 +586,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..ee0c5d2f2 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/AppPermissionGroupsViewModel.kt
@@ -46,6 +46,7 @@ import com.android.permissioncontroller.permission.data.PackagePermissionsLiveDa
import com.android.permissioncontroller.permission.data.PackagePermissionsLiveData.Companion.NON_RUNTIME_NORMAL_PERMS
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.permissioncontroller.permission.data.get
+import com.android.permissioncontroller.permission.data.v35.PackagePermissionsExternalDeviceLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo.PermGrantState
import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
import com.android.permissioncontroller.permission.ui.Category
@@ -55,6 +56,7 @@ import com.android.permissioncontroller.permission.utils.PermissionMapping
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.Utils.AppPermsLastAccessType
import com.android.permissioncontroller.permission.utils.navigateSafe
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils
import java.time.Instant
import java.util.concurrent.TimeUnit
import kotlin.math.max
@@ -93,137 +95,242 @@ class AppPermissionGroupsViewModel(
data class GroupUiInfo(
val groupName: String,
val isSystem: Boolean = false,
- val subtitle: PermSubtitle
+ val subtitle: PermSubtitle,
+ val persistentDeviceId: String,
) {
- constructor(groupName: String, isSystem: Boolean) :
- this(groupName, isSystem, PermSubtitle.NONE)
+ constructor(
+ groupName: String,
+ isSystem: Boolean
+ ) : this(
+ groupName,
+ isSystem,
+ PermSubtitle.NONE,
+ MultiDeviceUtils.getDefaultDevicePersistentDeviceId()
+ )
+
+ constructor(
+ groupName: String,
+ isSystem: Boolean,
+ subtitle: PermSubtitle,
+ ) : this(
+ groupName,
+ isSystem,
+ subtitle,
+ MultiDeviceUtils.getDefaultDevicePersistentDeviceId()
+ )
}
// 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
+ private val packagePermsExternalDeviceLiveData =
+ PackagePermissionsExternalDeviceLiveData[packageName, user]
/**
- * 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()
+ }
+ addSource(packagePermsExternalDeviceLiveData) { 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
- }
- if (groupName == Manifest.permission_group.STORAGE &&
- (fullStorageState?.isGranted == true && !fullStorageState.isLegacy)) {
- groupGrantStates[Category.ALLOWED]!!.add(
- GroupUiInfo(groupName, isSystem, PermSubtitle.ALL_FILES))
- return@let
+ val fullStorageState =
+ fullStoragePermsLiveData.value?.find { pkg ->
+ pkg.packageName == packageName && pkg.user == user
}
- 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)
+ )
}
+ 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)
+ )
+ }
+ }
+ }
+
+ packagePermsExternalDeviceLiveData.value?.forEach { externalDeviceGrantInfo ->
+ val groupName = externalDeviceGrantInfo.groupName
+ val isSystem =
+ PermissionMapping.getPlatformPermissionGroups().contains(groupName)
+ val persistentDeviceId = externalDeviceGrantInfo.persistentDeviceId
+ when (externalDeviceGrantInfo.permGrantState) {
+ PermGrantState.PERMS_ALLOWED -> {
groupGrantStates[Category.ALLOWED]!!.add(
- GroupUiInfo(groupName, isSystem, subtitle))
+ GroupUiInfo(
+ groupName,
+ isSystem,
+ PermSubtitle.NONE,
+ persistentDeviceId
+ )
+ )
}
- 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,
+ persistentDeviceId
+ )
+ )
+ PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY ->
+ groupGrantStates[Category.ALLOWED]!!.add(
+ GroupUiInfo(
+ groupName,
+ isSystem,
+ PermSubtitle.FOREGROUND_ONLY,
+ persistentDeviceId
+ )
+ )
+ PermGrantState.PERMS_DENIED ->
+ groupGrantStates[Category.DENIED]!!.add(
+ GroupUiInfo(
+ groupName,
+ isSystem,
+ PermSubtitle.NONE,
+ persistentDeviceId
+ )
+ )
+ PermGrantState.PERMS_ASK ->
+ groupGrantStates[Category.ASK]!!.add(
+ GroupUiInfo(
+ groupName,
+ isSystem,
+ PermSubtitle.NONE,
+ persistentDeviceId
+ )
+ )
}
}
- }
- 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 +340,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 +395,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 +425,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 99b40d8a7..5b6d8b8db 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,9 @@ 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.CAMERA
+import android.Manifest.permission_group.LOCATION
import android.Manifest.permission_group.READ_MEDIA_VISUAL
import android.annotation.SuppressLint
import android.app.Activity
@@ -28,19 +31,17 @@ 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.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener
+import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams
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 +57,15 @@ 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.data.v35.PackagePermissionsExternalDeviceLiveData
+import com.android.permissioncontroller.permission.model.livedatatypes.AppPermGroupUiInfo
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 +75,13 @@ 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
@@ -86,6 +89,7 @@ import com.android.permissioncontroller.permission.utils.SafetyNetLogger
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.permission.utils.navigateSafe
import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils
+import com.android.permissioncontroller.permission.utils.v35.MultiDeviceUtils
import com.android.settingslib.RestrictedLockUtils
import java.util.Random
import kotlin.collections.component1
@@ -100,19 +104,19 @@ import kotlin.collections.component2
* @param permGroupName The name of the permission group this ViewModel represents
* @param user The user of the package
* @param sessionId A session ID used in logs to identify this particular session
+ * @param persistentDeviceId The external device identifier
*/
class AppPermissionViewModel(
private val app: Application,
private val packageName: String,
private val permGroupName: String,
private val user: UserHandle,
- private val sessionId: Long
+ private val sessionId: Long,
+ private val persistentDeviceId: String
) : ViewModel() {
-
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 +138,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,90 +163,196 @@ 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
+ }
+ }
+
+ override fun onUpdate() {
+ if (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) {
+ return
+ }
+
+ val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
+ if (safetyLabel == null) {
+ value = false
+ return
+ }
+
+ value =
+ SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(
+ safetyLabel,
+ permGroupName
+ )
+ .any()
+ }
}
- init {
- if (safetyLabelInfoLiveData != null &&
- PermissionMapping.isSafetyLabelAwarePermissionGroup(permGroupName)) {
- addSource(safetyLabelInfoLiveData) { update() }
+ @get:RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ val sensorStatusLiveData: SensorStatusLiveData? by
+ lazy(LazyThreadSafetyMode.NONE) {
+ if (SdkLevel.isAtLeastV()) {
+ SensorStatusLiveData()
} else {
- value = false
+ null
}
}
- override fun onUpdate() {
- if (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale) {
- return
+ /** A LiveData that tracks whether to show or hide a warning banner for a sensor */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ inner class SensorStatusLiveData() : SmartUpdateMediatorLiveData<Boolean>() {
+ val sensorPrivacyManager = app.getSystemService(SensorPrivacyManager::class.java)!!
+ val sensor = Utils.getSensorCode(permGroupName)
+ val isLocation = LOCATION.equals(permGroupName)
+ val isCamera = CAMERA.equals(permGroupName)
+
+ init {
+ addSource(buttonStateLiveData) { update() }
+ checkAndUpdateStatus()
+ }
+
+ fun checkAndUpdateStatus(showBannerForSensorUpdate: Boolean? = null) {
+ var showBanner = showBannerForSensorUpdate ?: showBannerForSensor()
+ if (isPermissionDenied()) {
+ showBanner = false
}
+ value = showBanner
+ }
- val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
- if (safetyLabel == null) {
- value = false
- return
+ fun showBannerForSensor(): Boolean {
+ return if (isLocation) {
+ !LocationUtils.isLocationEnabled(app.getApplicationContext())
+ } else if (isCamera) {
+ val state =
+ sensorPrivacyManager.getSensorPrivacyState(
+ SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
+ SensorPrivacyManager.Sensors.CAMERA
+ )
+ state != SensorPrivacyManager.StateTypes.DISABLED
+ } else {
+ sensorPrivacyManager.isSensorPrivacyEnabled(sensor)
}
+ }
- value = SafetyLabelUtils.getSafetyLabelSharingPurposesForGroup(
- safetyLabel, permGroupName).any()
+ fun isPermissionDenied(): Boolean {
+ if (buttonStateLiveData.isInitialized) {
+ val buttonState = buttonStateLiveData.value
+ return buttonState?.get(DENY)?.isChecked == true ||
+ buttonState?.get(DENY_FOREGROUND)?.isChecked == true
+ }
+ return false
}
- }
- /**
- * A livedata which determines which detail string, if any, should be shown
- */
- val fullStorageStateLiveData = object : SmartUpdateMediatorLiveData<FullStoragePackageState>() {
- init {
- if (isStorageAndLessThanT) {
- addSource(FullStoragePermissionAppsLiveData) {
- update()
+ override fun onActive() {
+ super.onActive()
+ checkAndUpdateStatus()
+ if (isLocation) {
+ LocationUtils.addLocationListener(mainLocListener)
+ if (
+ LocationUtils.isAutomotiveLocationBypassAllowlistedPackage(
+ app.getApplicationContext(),
+ packageName
+ )
+ ) {
+ LocationUtils.addAutomotiveLocationBypassListener(locBypassListener)
}
} else {
- value = null
+ sensorPrivacyManager.addSensorPrivacyListener(sensor, sensorPrivacyListener)
}
}
- override fun onUpdate() {
- for (state in FullStoragePermissionAppsLiveData.value ?: return) {
- if (state.packageName == packageName && state.user == user) {
- value = state
- return
+
+ override fun onInactive() {
+ super.onInactive()
+ if (isLocation) {
+ LocationUtils.removeLocationListener(mainLocListener)
+ if (
+ LocationUtils.isAutomotiveLocationBypassAllowlistedPackage(
+ app.getApplicationContext(),
+ packageName
+ )
+ ) {
+ LocationUtils.removeAutomotiveLocationBypassListener(locBypassListener)
}
+ } else {
+ sensorPrivacyManager.removeSensorPrivacyListener(sensor, sensorPrivacyListener)
}
- value = null
- return
+ }
+
+ private val sensorPrivacyListener =
+ object : OnSensorPrivacyChangedListener {
+ override fun onSensorPrivacyChanged(params: SensorPrivacyChangedParams) {
+ val showBanner = (params.getState() != SensorPrivacyManager.StateTypes.DISABLED)
+ checkAndUpdateStatus(showBanner)
+ }
+
+ @Deprecated("Please use onSensorPrivacyChanged(SensorPrivacyChangedParams)")
+ override fun onSensorPrivacyChanged(sensor: Int, enabled: Boolean) {}
+ }
+
+ private val mainLocListener = { isEnabled: Boolean -> checkAndUpdateStatus(!isEnabled) }
+ private val locBypassListener = { _: Boolean -> checkAndUpdateStatus() }
+ override fun onUpdate() {
+ checkAndUpdateStatus()
}
}
+ /** 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
+ }
+ }
+
+ override fun onUpdate() {
+ for (state in FullStoragePermissionAppsLiveData.value ?: return) {
+ if (state.packageName == packageName && state.user == user) {
+ value = state
+ return
+ }
+ }
+ value = null
+ return
+ }
+ }
+
data class ButtonState(
var isChecked: Boolean,
var isEnabled: Boolean,
@@ -251,182 +362,271 @@ 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>>() {
-
- private val appPermGroupLiveData = LightAppPermGroupLiveData[packageName, permGroupName,
- user]
- private val mediaStorageSupergroupLiveData =
- mutableMapOf<String, LightAppPermGroupLiveData>()
-
- 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
+ /** 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 packagePermissionsExternalDeviceLiveData =
+ PackagePermissionsExternalDeviceLiveData[packageName, user]
+
+ 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()
}
- update()
}
- }
- if (isStorageAndLessThanT) {
- addSource(fullStorageStateLiveData) {
- update()
+ if (isStorageAndLessThanT) {
+ addSource(fullStorageStateLiveData) { 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)
+ 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)
+ }
}
}
- }
- addSource(showPermissionRationaleLiveData) {
- update()
- }
- }
+ addSource(showPermissionRationaleLiveData) { update() }
- private fun onMediaPermGroupUpdate(permGroupName: String, permGroup: LightAppPermGroup?) {
- if (permGroup == null) {
- mediaStorageSupergroupPermGroups.remove(permGroupName)
- value = null
- } else {
- mediaStorageSupergroupPermGroups[permGroupName] = permGroup
- update()
+ addSource(packagePermissionsExternalDeviceLiveData) { update() }
}
- }
- override fun onUpdate() {
- val group = appPermGroupLiveData.value ?: return
- for (mediaGroupLiveData in mediaStorageSupergroupLiveData.values) {
- if (!mediaGroupLiveData.isInitialized) {
- return
+ private fun onMediaPermGroupUpdate(
+ permGroupName: String,
+ permGroup: LightAppPermGroup?
+ ) {
+ if (permGroup == null) {
+ mediaStorageSupergroupPermGroups.remove(permGroupName)
+ value = null
+ } else {
+ mediaStorageSupergroupPermGroups[permGroupName] = permGroup
+ update()
}
}
- if (!showPermissionRationaleLiveData.isInitialized) {
- return
- }
+ // TODO: b/328839130 (Merge this with default device implementation)
+ private fun getButtonStatesForExternalDevicePermission(): Map<ButtonType, ButtonState> {
+ val allowedForegroundState = ButtonState()
+ allowedForegroundState.isShown = true
- val admin = RestrictedLockUtils.getProfileOrDeviceOwner(app, user)
+ val askState = ButtonState()
+ askState.isShown = true
- val allowedState = ButtonState()
- val allowedAlwaysState = ButtonState()
- val allowedForegroundState = ButtonState()
- val askOneTimeState = ButtonState()
- val askState = ButtonState()
- val deniedState = ButtonState()
- val deniedForegroundState = ButtonState()
- val selectState = ButtonState()
+ val deniedState = ButtonState()
+ deniedState.isShown = true
- askOneTimeState.isShown = group.foreground.isGranted && group.isOneTime
- askState.isShown = PermissionMapping.supportsOneTimeGrant(permGroupName) &&
- !(group.foreground.isGranted && group.isOneTime)
- deniedState.isShown = true
+ packagePermissionsExternalDeviceLiveData.value!!
+ .filter {
+ it.groupName == permGroupName && it.persistentDeviceId == persistentDeviceId
+ }
+ .map { it.permGrantState }
+ .forEach {
+ when (it) {
+ AppPermGroupUiInfo.PermGrantState.PERMS_ALLOWED_FOREGROUND_ONLY ->
+ allowedForegroundState.isChecked = true
+ AppPermGroupUiInfo.PermGrantState.PERMS_ASK -> askState.isChecked = true
+ AppPermGroupUiInfo.PermGrantState.PERMS_DENIED ->
+ deniedState.isChecked = true
+ else -> {
+ Log.e(LOG_TAG, "Unsupported PermGrantState=$it")
+ }
+ }
+ }
+ return mapOf(
+ ALLOW to ButtonState(),
+ ALLOW_ALWAYS to ButtonState(),
+ ALLOW_FOREGROUND to allowedForegroundState,
+ ASK_ONCE to ButtonState(),
+ ASK to askState,
+ DENY to deniedState,
+ DENY_FOREGROUND to ButtonState(),
+ LOCATION_ACCURACY to ButtonState(),
+ SELECT_PHOTOS to ButtonState()
+ )
+ }
- if (group.hasPermWithBackgroundMode) {
- // Background / Foreground / Deny case
- allowedForegroundState.isShown = true
- if (group.hasBackgroundGroup) {
- allowedAlwaysState.isShown = true
+ override fun onUpdate() {
+ if (!MultiDeviceUtils.isDefaultDeviceId(persistentDeviceId)) {
+ value = getButtonStatesForExternalDevicePermission()
+ return
}
- 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
+ val group = appPermGroupLiveData.value ?: return
+
+ for (mediaGroupLiveData in mediaStorageSupergroupLiveData.values) {
+ if (!mediaGroupLiveData.isInitialized) {
+ return
}
- } 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
+
+ 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
- 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
+ if (group.hasPermWithBackgroundMode) {
+ // Background / Foreground / Deny case
+ allowedForegroundState.isShown = true
+ if (group.hasBackgroundGroup) {
+ allowedAlwaysState.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
+ }
+ } else if (
+ Utils.areGroupPermissionsIndividuallyControlled(app, permGroupName)
+ ) {
+ val detailId = getIndividualPermissionDetailResId(group)
+ detailResIdLiveData.value = detailId.first to detailId.second
+ }
+ } else if (
+ shouldShowPhotoPickerPromptForApp(group) &&
+ group.permGroupName == READ_MEDIA_VISUAL
+ ) {
+ // 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 (
+ Utils.getApplicationEnhancedConfirmationRestrictedIntentAsUser(
+ user,
+ app,
+ packageName,
+ permGroupName
+ ) != null
+ ) {
+ allowedState.isEnabled = false
+ }
+ 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 +634,87 @@ 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 =
+ isLocationAccuracyAvailableForApp(group) &&
+ 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) {
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.VANILLA_ICE_CREAM, codename = "VanillaIceCream")
+ fun handleDisabledAllowButton(fragment: Fragment) {
+ if (
+ lightAppPermGroup!!.foreground.isSystemFixed ||
+ lightAppPermGroup!!.foreground.isPolicyFixed
+ )
return
- }
- photoPickerLauncher = fragment.registerForActivityResult(
- object : ActivityResultContract<Unit, Int>() {
- override fun parseResult(resultCode: Int, intent: Intent?): Int {
- return resultCode
- }
+ val restrictionIntent =
+ Utils.getApplicationEnhancedConfirmationRestrictedIntentAsUser(
+ user,
+ app,
+ packageName,
+ permGroupName
+ )
+ ?: return
+ fragment.startActivity(restrictionIntent)
+ }
- 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)
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codename = "UpsideDownCake")
+ 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 isLocationAccuracyAvailableForApp(group: LightAppPermGroup): Boolean {
+ return isLocationAccuracyEnabled() &&
+ group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.S
}
private fun isFineLocationChecked(group: LightAppPermGroup): Boolean {
@@ -501,14 +726,17 @@ 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.isGrantedIncludingAppOp ||
- coarseLocation.isGrantedIncludingAppOp) {
+ return if (
+ fineLocation.isGrantedIncludingAppOp || coarseLocation.isGrantedIncludingAppOp
+ ) {
fineLocation.isGrantedIncludingAppOp
- } else if (fineLocation.isSelectedLocationAccuracy ||
- coarseLocation.isSelectedLocationAccuracy) {
+ } else if (
+ fineLocation.isSelectedLocationAccuracy || coarseLocation.isSelectedLocationAccuracy
+ ) {
fineLocation.isSelectedLocationAccuracy
} else {
- getDefaultPrecision()
+ // default location precision is true, indicates FINE
+ true
}
}
return false
@@ -517,7 +745,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 +836,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 +852,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 +866,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(
@@ -669,6 +907,11 @@ class AppPermissionViewModel(
val wasForegroundGranted = group.foreground.isGranted
val wasBackgroundGranted = group.background.isGranted
+ if (!MultiDeviceUtils.isDefaultDeviceId(persistentDeviceId)) {
+ handleChangeForExternalDevice(group.permissions.keys, changeRequest, setOneTime)
+ return
+ }
+
if (LocationUtils.isLocationGroupAndProvider(context, permGroupName, packageName)) {
val packageLabel = KotlinUtils.getPackageLabel(app, packageName, user)
LocationUtils.showLocationDialog(context, packageLabel)
@@ -685,8 +928,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 +943,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 +968,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 +1002,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 +1027,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 +1061,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 +1083,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 +1103,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 +1129,50 @@ class AppPermissionViewModel(
logPermissionChanges(oldGroup, newGroup, buttonClicked)
- fullStorageStateLiveData.value?.let {
- FullStoragePermissionAppsLiveData.recalculate()
- }
+ fullStorageStateLiveData.value?.let { FullStoragePermissionAppsLiveData.recalculate() }
}
}
+ /**
+ * Handles the permission change for external devices. The original method that handles
+ * permission change for the default device makes use of LightAppPermGroup. This data class is
+ * not available for external devices, hence this implementation makes use of persistentDeviceId
+ * specific methods.
+ *
+ * TODO: b/328839130
+ */
+ private fun handleChangeForExternalDevice(
+ permissions: Set<String>,
+ changeRequest: ChangeRequest,
+ setOneTime: Boolean
+ ) {
+ when (changeRequest) {
+ ChangeRequest.GRANT_FOREGROUND_ONLY ->
+ MultiDeviceUtils.grantRuntimePermissionsWithPersistentDeviceId(
+ app,
+ persistentDeviceId,
+ packageName,
+ permissions,
+ true
+ )
+ ChangeRequest.REVOKE_BOTH ->
+ MultiDeviceUtils.revokeRuntimePermissionsWithPersistentDeviceId(
+ app,
+ persistentDeviceId,
+ packageName,
+ permissions,
+ !setOneTime,
+ setOneTime
+ )
+ else -> Log.e(LOG_TAG, "Unsupported changeRequest=$changeRequest")
+ }
+ PackagePermissionsExternalDeviceLiveData[packageName, user].update()
+ }
+
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 +1182,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 +1194,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 +1221,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 +1305,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 +1319,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 +1346,7 @@ class AppPermissionViewModel(
hasConfirmedRevoke = true
}
- fullStorageStateLiveData.value?.let {
- FullStoragePermissionAppsLiveData.recalculate()
- }
+ fullStorageStateLiveData.value?.let { FullStoragePermissionAppsLiveData.recalculate() }
}
}
@@ -1017,11 +1358,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 +1381,9 @@ class AppPermissionViewModel(
}
private fun getIndividualPermissionDetailResId(group: LightAppPermGroup): Pair<Int, Int> {
- return when (val numRevoked =
- group.permissions.filter { !it.value.isGrantedIncludingAppOp }.size) {
+ return when (
+ val numRevoked = group.permissions.filter { !it.value.isGrantedIncludingAppOp }.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,16 +1398,16 @@ 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) {
// Permission is fully controlled by policy and cannot be switched
if (isForegroundPolicyDenied) {
- return R.string.disabled_by_admin
+ return com.android.settingslib.widget.restricted.R.string.disabled_by_admin
} else if (isPolicyFullyFixedWithGrantedOrNoBkg) {
- return R.string.enabled_by_admin
+ return com.android.settingslib.widget.restricted.R.string.enabled_by_admin
} else if (group.isPolicyFullyFixed) {
return R.string.permission_summary_enabled_by_admin_foreground_only
}
@@ -1110,11 +1453,17 @@ class AppPermissionViewModel(
for ((permName, permission) in oldGroup.permissions) {
val newPermission = newGroup.permissions[permName] ?: continue
- if (permission.isGrantedIncludingAppOp != newPermission.isGrantedIncludingAppOp ||
- permission.flags != newPermission.flags) {
+ if (
+ permission.isGrantedIncludingAppOp != newPermission.isGrantedIncludingAppOp ||
+ permission.flags != newPermission.flags
+ ) {
logAppPermissionFragmentActionReported(changeId, newPermission, buttonPressed)
- PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext,
- packageName, permGroupName, newPermission.isGrantedIncludingAppOp)
+ PermissionDecisionStorageImpl.recordPermissionDecision(
+ app.applicationContext,
+ packageName,
+ permGroupName,
+ newPermission.isGrantedIncludingAppOp
+ )
PermissionChangeStorageImpl.recordPermissionChange(packageName)
}
}
@@ -1136,13 +1485,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.isGrantedIncludingAppOp, 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.isGrantedIncludingAppOp + " permissionFlags=" +
- permission.flags + " buttonPressed=$buttonPressed")
+ PermissionControllerStatsLog.write(
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED,
+ sessionId,
+ changeId,
+ uid,
+ packageName,
+ permission.permInfo.name,
+ permission.isGrantedIncludingAppOp,
+ permission.flags,
+ buttonPressed
+ )
+ Log.i(
+ LOG_TAG,
+ "Permission changed via UI with sessionId=$sessionId changeId=" +
+ "$changeId uid=$uid packageName=$packageName permission=" +
+ permission.permInfo.name +
+ " isGranted=" +
+ permission.isGrantedIncludingAppOp +
+ " permissionFlags=" +
+ permission.flags +
+ " buttonPressed=$buttonPressed"
+ )
}
/** Logs information about this AppPermissionGroup and view session */
@@ -1156,19 +1520,20 @@ class AppPermissionViewModel(
uid,
packageName,
permGroupName,
- permissionRationaleShown)
- Log.v(
+ permissionRationaleShown
+ )
+ Log.i(
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 +1542,10 @@ class AppPermissionViewModel(
val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
- return group.isGranted && group.permissions.values.all {
- it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp)
- }
+ return group.isGranted &&
+ group.permissions.values.all {
+ it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp)
+ }
}
}
@@ -1191,16 +1557,41 @@ class AppPermissionViewModel(
* @param permGroupName The name of the permission group this ViewModel represents
* @param user The user of the package
* @param sessionId A session ID used in logs to identify this particular session
+ * @param persistentDeviceId Indicates the device in the context of virtual devices
*/
class AppPermissionViewModelFactory(
private val app: Application,
private val packageName: String,
private val permGroupName: String,
private val user: UserHandle,
- private val sessionId: Long
+ private val sessionId: Long,
+ private val persistentDeviceId: String
) : ViewModelProvider.Factory {
+ constructor(
+ app: Application,
+ packageName: String,
+ permGroupName: String,
+ user: UserHandle,
+ sessionId: Long
+ ) : this(
+ app,
+ packageName,
+ permGroupName,
+ user,
+ sessionId,
+ MultiDeviceUtils.getDefaultDevicePersistentDeviceId()
+ )
+
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@Suppress("UNCHECKED_CAST")
- return AppPermissionViewModel(app, packageName, permGroupName, user, sessionId) as T
+ return AppPermissionViewModel(
+ app,
+ packageName,
+ permGroupName,
+ user,
+ sessionId,
+ persistentDeviceId
+ )
+ as T
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
index 0680ffcd2..b5df6f410 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/GrantPermissionsViewModel.kt
@@ -17,13 +17,14 @@
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.POST_NOTIFICATIONS
import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
import android.Manifest.permission_group.LOCATION
+import android.Manifest.permission_group.NOTIFICATIONS
+import android.Manifest.permission_group.READ_MEDIA_AURAL
import android.Manifest.permission_group.READ_MEDIA_VISUAL
+import android.Manifest.permission_group.STORAGE
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
@@ -39,11 +40,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
@@ -70,12 +68,13 @@ import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED
import com.android.permissioncontroller.auto.DrivingDecisionReminderService
+import com.android.permissioncontroller.ecm.EnhancedConfirmationStatsLogUtils
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
@@ -84,29 +83,7 @@ import com.android.permissioncontroller.permission.service.PermissionChangeStora
import com.android.permissioncontroller.permission.service.v33.PermissionDecisionStorageImpl
import com.android.permissioncontroller.permission.ui.AutoGrantPermissionsNotifier
import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON
import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.INTENT_PHOTOS_SELECTED
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LINK_TO_PERMISSION_RATIONALE
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LINK_TO_SETTINGS
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
-import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN
@@ -118,58 +95,83 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandle
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.model.grantPermissions.BackgroundGrantBehavior
+import com.android.permissioncontroller.permission.ui.model.grantPermissions.BasicGrantBehavior
+import com.android.permissioncontroller.permission.ui.model.grantPermissions.GrantBehavior
+import com.android.permissioncontroller.permission.ui.model.grantPermissions.HealthGrantBehavior
+import com.android.permissioncontroller.permission.ui.model.grantPermissions.LocationGrantBehavior
+import com.android.permissioncontroller.permission.ui.model.grantPermissions.NotificationGrantBehavior
+import com.android.permissioncontroller.permission.ui.model.grantPermissions.StorageGrantBehavior
import com.android.permissioncontroller.permission.ui.v34.PermissionRationaleActivity
-import com.android.permissioncontroller.permission.utils.v31.AdminRestrictedPermissionsUtils
+import com.android.permissioncontroller.permission.utils.ContextCompat
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.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
* @param requestedPermissions: The list of permissions requested
+ * @param systemRequestedPermissions: The list of permissions requested as a result of a system
+ * triggered dialog, not an app-triggered dialog
* @param sessionId: A long to identify this session
* @param storedState: Previous state, if this activity was stopped and is being recreated
*/
class GrantPermissionsViewModel(
private val app: Application,
private val packageName: String,
+ private val deviceId: Int,
private val requestedPermissions: List<String>,
+ private val systemRequestedPermissions: List<String>,
private val sessionId: Long,
private val storedState: Bundle?
) : ViewModel() {
private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
private val user = Process.myUserHandle()
- private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user]
+ private val packageInfoLiveData = LightPackageInfoLiveData[packageName, user, deviceId]
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
}
private val dpm = app.getSystemService(DevicePolicyManager::class.java)!!
private val permissionPolicy = dpm.getPermissionPolicy(null)
- private val permGroupsToSkip = mutableListOf<String>()
- private var groupStates = mutableMapOf<Pair<String, Boolean>, GroupState>()
+ private val groupStates = mutableMapOf<String, GroupState>()
private var autoGrantNotifier: AutoGrantPermissionsNotifier? = null
- private fun getAutoGrantNotifier(): AutoGrantPermissionsNotifier {
- autoGrantNotifier = AutoGrantPermissionsNotifier(app, packageInfo.toPackageInfo(app)!!)
+
+ private fun getAutoGrantNotifier(): AutoGrantPermissionsNotifier? {
+ var fullPackageInfo = packageInfo.toPackageInfo(app)
+ if (fullPackageInfo == null) {
+ // try twice
+ fullPackageInfo = packageInfo.toPackageInfo(app)
+ }
+ if (fullPackageInfo == null) {
+ // We've tried to get our package info twice, and failed twice. Close the grant dialog,
+ // because the app is not accessible.
+ requestInfosLiveData.value = null
+ return null
+ }
+ autoGrantNotifier = AutoGrantPermissionsNotifier(app, fullPackageInfo)
return autoGrantNotifier!!
}
@@ -179,447 +181,254 @@ class GrantPermissionsViewModel(
// filtering system fixed, auto grant, etc.
private var unfilteredAffectedPermissions = requestedPermissions
- private val splitPermissionTargetSdkMap = mutableMapOf<String, Int>()
-
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.
+ * An internal class which represents the state of a current AppPermissionGroup grant request.
+ * It is made up of the following:
+ *
+ * @param group The LightAppPermGroup representing the current state of the permissions for this
+ * app
+ * @param affectedPermissions The permissions that should be affected by this
*/
+ internal class GroupState(
+ internal val group: LightAppPermGroup,
+ internal val affectedPermissions: MutableSet<String> = mutableSetOf(),
+ internal var state: Int = STATE_UNKNOWN,
+ ) {
+ val fgPermissions = affectedPermissions - group.backgroundPermNames.toSet()
+ val bgPermissions = affectedPermissions - fgPermissions
+
+ override fun toString(): String {
+ val stateStr: String =
+ when (state) {
+ STATE_UNKNOWN -> "unknown"
+ STATE_GRANTED -> "granted"
+ STATE_DENIED -> "denied"
+ STATE_FG_GRANTED_BG_UNKNOWN -> "foreground granted, background unknown"
+ else -> "skipped"
+ }
+ return "${group.permGroupName} $stateStr $affectedPermissions"
+ }
+ }
+
data class RequestInfo(
val groupInfo: LightPermGroupInfo,
- val buttonVisibilities: List<Boolean> = List(NEXT_BUTTON) { false },
- val locationVisibilities: List<Boolean> = List(NEXT_LOCATION_DIALOG) { false },
- val message: RequestMessage = RequestMessage.FG_MESSAGE,
- val detailMessage: RequestMessage = RequestMessage.NO_MESSAGE,
- val sendToSettingsImmediately: Boolean = false,
- val openPhotoPicker: Boolean = false,
+ val prompt: Prompt,
+ val deny: DenyButton,
+ val showRationale: Boolean,
+ val deviceId: Int = ContextCompat.DEVICE_ID_DEFAULT
) {
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()
- }
+ val requestInfosLiveData =
+ object : SmartUpdateMediatorLiveData<List<RequestInfo>>() {
+ private val LOG_TAG = GrantPermissionsViewModel::class.java.simpleName
+ private val packagePermissionsLiveData = PackagePermissionsLiveData[packageName, user]
- private fun onPackageLoaded() {
- if (packageInfoLiveData.isStale ||
- packagePermissionsLiveData.isStale ||
- (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale)) {
- return
- }
+ init {
+ addSource(packagePermissionsLiveData) { onPackageLoaded() }
+ addSource(packageInfoLiveData) { onPackageLoaded() }
+ if (safetyLabelInfoLiveData != null) {
+ addSource(safetyLabelInfoLiveData) { 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
+ // Load package state, if available
+ onPackageLoaded()
}
- 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
- }
+ private fun onPackageLoaded() {
+ if (
+ packageInfoLiveData.isStale ||
+ packagePermissionsLiveData.isStale ||
+ (safetyLabelInfoLiveData != null && safetyLabelInfoLiveData.isStale)
+ ) {
+ return
+ }
- val allAffectedPermissions = requestedPermissions.toMutableSet()
- for (requestedPerm in requestedPermissions) {
- allAffectedPermissions.addAll(computeAffectedPermissions(requestedPerm, groups))
- }
- unfilteredAffectedPermissions = allAffectedPermissions.toList()
+ val groups = packagePermissionsLiveData.value
+ val pI = packageInfoLiveData.value
+ if (groups.isNullOrEmpty() || 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
+ }
- setAppPermGroupsLiveDatas(groups.toMutableMap().apply {
- remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
- })
+ val affectedPermissions = requestedPermissions.toMutableSet()
+ for (requestedPerm in requestedPermissions) {
+ affectedPermissions.addAll(getAffectedSplitPermissions(requestedPerm))
+ }
+ if (packageInfo.targetSdkVersion < Build.VERSION_CODES.O) {
+ // For < O apps all permissions of the groups of the requested ones are affected
+ for (affectedPerm in affectedPermissions.toSet()) {
+ val otherGroupPerms =
+ groups.values.firstOrNull { affectedPerm in it } ?: emptyList()
+ affectedPermissions.addAll(otherGroupPerms)
+ }
+ }
+ unfilteredAffectedPermissions = affectedPermissions.toList()
- for (splitPerm in app.getSystemService(
- PermissionManager::class.java)!!.splitPermissions) {
- splitPermissionTargetSdkMap[splitPerm.splitPermission] = splitPerm.targetSdk
+ setAppPermGroupsLiveDatas(
+ groups.toMutableMap().apply {
+ remove(PackagePermissionsLiveData.NON_RUNTIME_NORMAL_PERMS)
+ }
+ )
}
- }
-
- private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) {
- val requestedGroups = groups.filter { (_, perms) ->
- perms.any { it in unfilteredAffectedPermissions }
- }
+ private fun setAppPermGroupsLiveDatas(groups: Map<String, List<String>>) {
+ 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, deviceId]
+ }
+ 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) {
+ 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) {
Log.e(LOG_TAG, "Group $packageName $groupName invalid")
+ groupStates[groupName]?.state = STATE_SKIPPED
+ continue
}
- groupStates[groupName to true]?.state = STATE_SKIPPED
- groupStates[groupName to false]?.state = STATE_SKIPPED
- continue
- }
- packageInfo = appPermGroup.packageInfo
+ 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]?.isGrantedIncludingAppOp == true &&
- appPermGroup.permissions[perm]?.isRevokeWhenRequested == false
- }
+ val state = groupStates[groupName]
+ if (state != null) {
+ val allAffectedGranted =
+ state.affectedPermissions.all { perm ->
+ appPermGroup.permissions[perm]?.isGrantedIncludingAppOp == true &&
+ appPermGroup.permissions[perm]?.isRevokeWhenRequested == false
+ }
if (allAffectedGranted) {
- groupStates[key]!!.state = STATE_ALLOWED
+ groupStates[groupName]!!.state = STATE_GRANTED
}
+ } else {
+ newGroups = true
}
- } else {
- newGroups = true
}
- }
-
- if (newGroups) {
- groupStates = getRequiredGroupStates(
- appPermGroupLiveDatas.mapNotNull { it.value.value })
- }
- setRequestInfosFromGroupStates()
- }
- 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]?.isGrantedIncludingAppOp == 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
+ if (newGroups) {
+ addRequiredGroupStates(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.isGrantedIncludingAppOp) ||
- groupState.affectedPermissions == listOf(READ_MEDIA_VISUAL_USER_SELECTED)) {
- requestInfos.add(RequestInfo(groupInfo, openPhotoPicker = true))
+ private fun setRequestInfosFromGroupStates() {
+ val requestInfos = mutableListOf<RequestInfo>()
+ for (groupState in groupStates.values) {
+ if (!isStateUnknown(groupState.state)) {
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] =
- isFgUserSet
- 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 {
- 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
- 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 {
- // 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
}
- }
- 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?.isGrantedIncludingAppOp == 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 {
- locationVisibilities[COARSE_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
- }
+ val behavior = getGrantBehavior(groupState.group)
+ val isSystemTriggered =
+ groupState.affectedPermissions.any { it in systemRequestedPermissions }
+ val prompt =
+ behavior.getPrompt(
+ groupState.group,
+ groupState.affectedPermissions,
+ isSystemTriggered
+ )
+ if (prompt == Prompt.NO_UI_REJECT_ALL_GROUPS) {
+ value = null
+ return
}
- }
-
- 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) {
+ if (prompt == Prompt.NO_UI_REJECT_THIS_GROUP) {
+ reportRequestResult(
+ groupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED
+ )
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)
-
- 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
+ val denyBehavior =
+ behavior.getDenyButton(
+ groupState.group,
+ groupState.affectedPermissions,
+ prompt
+ )
+ val safetyLabel = safetyLabelInfoLiveData?.value?.safetyLabel
+ requestInfos.add(
+ RequestInfo(
+ groupState.group.permGroupInfo,
+ prompt,
+ denyBehavior,
+ shouldShowPermissionRationale(
+ safetyLabel,
+ groupState.group.permGroupName
+ ),
+ deviceId
+ )
+ )
+ }
+ sortPermissionGroups(requestInfos)
+
+ value =
+ if (
+ requestInfos.any { it.prompt == Prompt.NO_UI_SETTINGS_REDIRECT } &&
+ requestInfos.size > 1
+ ) {
+ Log.e(
+ LOG_TAG,
+ "For R+ apps, background permissions must be requested " +
+ "individually"
+ )
+ null
+ } else {
+ requestInfos
+ }
}
}
- }
- fun sortPermissionGroups(requestInfos: MutableList<RequestInfo>) {
+ private fun sortPermissionGroups(requestInfos: MutableList<RequestInfo>) {
requestInfos.sortWith { rhs, lhs ->
- val rhsHasOneTime = rhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
- val lhsHasOneTime = lhs.buttonVisibilities[ALLOW_ONE_TIME_BUTTON]
+ val rhsHasOneTime = isOneTimePrompt(rhs.prompt)
+ val lhsHasOneTime = isOneTimePrompt(lhs.prompt)
if (rhsHasOneTime && !lhsHasOneTime) {
-1
- } else if ((!rhsHasOneTime && lhsHasOneTime) ||
- isHealthPermissionGroup(rhs.groupName)
+ } else if (
+ (!rhsHasOneTime && lhsHasOneTime) || Utils.isHealthPermissionGroup(rhs.groupName)
) {
1
} else {
@@ -628,6 +437,18 @@ class GrantPermissionsViewModel(
}
}
+ private fun isOneTimePrompt(prompt: Prompt): Boolean {
+ return prompt in
+ setOf(
+ Prompt.ONE_TIME_FG,
+ Prompt.SETTINGS_LINK_WITH_OT,
+ Prompt.LOCATION_TWO_BUTTON_COARSE_HIGHLIGHT,
+ Prompt.LOCATION_TWO_BUTTON_FINE_HIGHLIGHT,
+ Prompt.LOCATION_COARSE_ONLY,
+ Prompt.LOCATION_FINE_UPGRADE
+ )
+ }
+
private fun shouldShowPermissionRationale(
safetyLabel: SafetyLabel?,
permissionGroupName: String?
@@ -636,70 +457,65 @@ 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, and adds new GroupState
+ * objects to the tracked 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)
- }
+ private fun addRequiredGroupStates(groups: List<LightAppPermGroup>) {
+ val filteredPermissions =
+ unfilteredAffectedPermissions.filter { perm ->
+ val group = getGroupWithPerm(perm, groups)
+ group != null && isPermissionGrantableAndNotFixed(perm, group)
+ }
+ val newGroupStates = mutableMapOf<String, GroupState>()
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 oldGroupState = groupStates[group.permGroupName]
+ if (!isStateUnknown(oldGroupState?.state)) {
+ // we've already dealt with this group
+ continue
}
- var currGroupState = groupStateInfo.state
- if (storedState != null && currGroupState != STATE_UNKNOWN) {
- currGroupState = storedState.getInt(getInstanceStateKey(group.permGroupName,
- isBackground), STATE_UNKNOWN)
+ val groupState = newGroupStates.getOrPut(group.permGroupName) { GroupState(group) }
+
+ var currGroupState = groupState.state
+ if (storedState != null && !isStateUnknown(groupState.state)) {
+ currGroupState = storedState.getInt(group.permGroupName, STATE_UNKNOWN)
}
- val otherGroupPermissions = filteredPermissions.filter { it in group.permissions }
- val groupStateOfPerm = getGroupState(perm, group, otherGroupPermissions)
+ val otherAffectedPermissionsInGroup =
+ filteredPermissions.filter { it in group.permissions }.toSet()
+ val groupStateOfPerm = getGroupState(perm, group, otherAffectedPermissionsInGroup)
if (groupStateOfPerm != STATE_UNKNOWN) {
+ // update the state if it is allowed, denied, or granted in foreground
currGroupState = groupStateOfPerm
}
- if (group.permGroupName in permGroupsToSkip) {
- currGroupState = STATE_SKIPPED
- }
-
if (currGroupState != STATE_UNKNOWN) {
- groupStateInfo.state = currGroupState
+ groupState.state = currGroupState
}
- // If we saved state, load it
- groupStateInfo.affectedPermissions.add(perm)
+
+ groupState.affectedPermissions.add(perm)
}
- return groupStates
+ newGroupStates.forEach { (groupName, groupState) -> groupStates[groupName] = groupState }
}
/**
- * Get the actually requested permissions when a permission is requested.
- *
- * >In some cases requesting to grant a single permission requires the system to grant
- * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole
- * group to be granted. Another case are permissions that are split into two. For apps that
- * target an SDK before the split, this method automatically adds the split off permission.
+ * Add additional permissions that should be granted in this request. For permissions that have
+ * split permissions, and apps that target an SDK before the split, this method automatically
+ * adds the split off permission.
*
* @param perm The requested permission
- *
- * @return The actually requested permissions
+ * @return The requested permissions plus any needed split permissions
*/
- private fun computeAffectedPermissions(
+ private fun getAffectedSplitPermissions(
perm: String,
- appPermissions: Map<String, List<String>>
): List<String> {
val requestingAppTargetSDK = packageInfo.targetSdkVersion
@@ -709,53 +525,30 @@ class GrantPermissionsViewModel(
val splitPerms = app.getSystemService(PermissionManager::class.java)!!.splitPermissions
for (splitPerm in splitPerms) {
-
if (requestingAppTargetSDK < splitPerm.targetSdk && perm == splitPerm.splitPermission) {
extendedBySplitPerms.addAll(splitPerm.newPermissions)
}
}
-
- // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected
- if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) {
- val extendedBySplitPermsAndGroup = mutableListOf<String>()
-
- for (splitPerm in extendedBySplitPerms) {
- val groups = appPermissions.filter { splitPerm in it.value }
- if (groups.isEmpty()) {
- continue
- }
-
- val permissionsInGroup = groups.values.first()
- for (permissionInGroup in permissionsInGroup) {
- extendedBySplitPermsAndGroup.add(permissionInGroup)
- }
- }
-
- return extendedBySplitPermsAndGroup
- } else {
- return extendedBySplitPerms
- }
+ return extendedBySplitPerms
}
private fun isPermissionGrantableAndNotFixed(perm: String, group: LightAppPermGroup): Boolean {
-
// 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
}
- if (HEALTH_PERMISSION_GROUP == group.permGroupName) {
- 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
@@ -763,27 +556,22 @@ class GrantPermissionsViewModel(
reportRequestResult(perm, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED)
// Skip showing groups that we know cannot be granted.
return false
- } else if (subGroup.isUserFixed) {
- if (perm == ACCESS_COARSE_LOCATION) {
- val coarsePerm = group.permissions[perm]
- if (coarsePerm != null && !coarsePerm.isUserFixed) {
- // If the location group is user fixed but ACCESS_COARSE_LOCATION is not, then
- // ACCESS_FINE_LOCATION must be user fixed. In this case ACCESS_COARSE_LOCATION
- // is still grantable.
- return true
- }
- } else if (perm in getPartialStorageGrantPermissionsForGroup(group) &&
- lightPermission.isGrantedIncludingAppOp) {
- // 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)
+ }
+
+ if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) {
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED
+ )
return false
- } else if (subGroup.isPolicyFixed && !subGroup.isGranted || lightPermission.isPolicyFixed) {
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_POLICY_FIXED)
+ }
+
+ val behavior = getGrantBehavior(group)
+ if (behavior.isPermissionFixed(group, perm)) {
+ reportRequestResult(
+ perm,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_FIXED
+ )
return false
}
@@ -793,151 +581,94 @@ class GrantPermissionsViewModel(
private fun getGroupState(
perm: String,
group: LightAppPermGroup,
- groupRequestedPermissions: List<String>
+ groupRequestedPermissions: Set<String>
): Int {
val policyState = getStateFromPolicy(perm, group)
- if (policyState != STATE_UNKNOWN) {
+ if (!isStateUnknown(policyState)) {
return policyState
}
- 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
- }
- 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,
- // then skip the request
- return STATE_SKIPPED
- }
- }
-
val isBackground = perm 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.isGrantedExcludingRWROrAllRWR &&
- !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.isGrantedExcludingRWROrAllRWR ||
- !isBackground && group.foreground.isGrantedExcludingRWROrAllRWR) &&
- canAutoGrantWholeGroup(group)) {
+ val behavior = getGrantBehavior(group)
+ return if (behavior.isGroupFullyGranted(group, groupRequestedPermissions)) {
if (group.permissions[perm]?.isGrantedIncludingAppOp == 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)
- }
-
- return if (storedState == null) {
- STATE_SKIPPED
- } else {
- STATE_ALLOWED
+ 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 STATE_UNKNOWN
- }
-
- /**
- * 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]?.isGrantedIncludingAppOp == false) {
- return false
- }
- // If READ_MEDIA_VISUAL_USER_SELECTED is the only permission in the group that is granted,
- // do not grant.
- if (isPartialStorageGrant(group) || HEALTH_PERMISSION_GROUP == group.permGroupName) {
- return false
- }
- return true
- }
-
- /**
- * 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) {
- return false
- }
-
- val partialPerms = getPartialStorageGrantPermissionsForGroup(group)
- return group.isGranted && group.permissions.values.all {
- it.name in partialPerms || (it.name !in partialPerms && !it.isGrantedIncludingAppOp)
+ STATE_GRANTED
+ } else if (behavior.isForegroundFullyGranted(group, groupRequestedPermissions)) {
+ STATE_FG_GRANTED_BG_UNKNOWN
+ } else {
+ STATE_UNKNOWN
}
}
private fun getStateFromPolicy(perm: String, group: LightAppPermGroup): Int {
val isBackground = perm in group.backgroundPermNames
- var skipGroup = false
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))
- state = STATE_ALLOWED
- skipGroup = true
-
- getAutoGrantNotifier().onPermissionAutoGranted(perm)
- reportRequestResult(perm,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__AUTO_GRANTED)
+ 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_GRANTED
+ getAutoGrantNotifier()?.onPermissionAutoGranted(perm)
+ 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) {
- return STATE_SKIPPED
- }
return state
}
@@ -967,98 +698,83 @@ 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
}
- val foregroundGroupState = groupStates[groupName to false]
- val backgroundGroupState = groupStates[groupName to true]
+ val groupState = groupStates[groupName] ?: return
when (result) {
CANCELED -> {
- if (foregroundGroupState != null) {
- 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)
- backgroundGroupState.state = STATE_SKIPPED
- }
+ reportRequestResult(
+ groupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED
+ )
+ groupState.state = STATE_SKIPPED
requestInfosLiveData.update()
return
}
GRANTED_ALWAYS -> {
- if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = false,
- doNotAskAgain = false)
- }
- if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = false,
- doNotAskAgain = false)
- }
+ onPermissionGrantResultSingleState(
+ groupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = false,
+ foregroundOnly = false,
+ doNotAskAgain = false
+ )
}
GRANTED_FOREGROUND_ONLY -> {
- if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = false,
- doNotAskAgain = false)
- }
- if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = false)
- }
+ onPermissionGrantResultSingleState(
+ groupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = false,
+ foregroundOnly = true,
+ doNotAskAgain = false
+ )
}
GRANTED_ONE_TIME -> {
- if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = true, isOneTime = true,
- doNotAskAgain = false)
- }
- if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = true,
- doNotAskAgain = false)
- }
+ onPermissionGrantResultSingleState(
+ groupState,
+ affectedForegroundPermissions,
+ granted = true,
+ isOneTime = true,
+ foregroundOnly = false,
+ doNotAskAgain = false
+ )
}
- GRANTED_USER_SELECTED, DENIED_MORE -> {
- if (foregroundGroupState != null) {
- grantUserSelectedVisualGroupPermissions(foregroundGroupState)
- }
+ GRANTED_USER_SELECTED,
+ DENIED_MORE -> {
+ grantUserSelectedVisualGroupPermissions(groupState)
}
DENIED -> {
- if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = false)
- }
- if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = false)
- }
+ onPermissionGrantResultSingleState(
+ groupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ foregroundOnly = false,
+ doNotAskAgain = false
+ )
}
DENIED_DO_NOT_ASK_AGAIN -> {
- if (foregroundGroupState != null) {
- onPermissionGrantResultSingleState(foregroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = true)
- }
- if (backgroundGroupState != null) {
- onPermissionGrantResultSingleState(backgroundGroupState,
- affectedForegroundPermissions, granted = false, isOneTime = false,
- doNotAskAgain = true)
- }
+ onPermissionGrantResultSingleState(
+ groupState,
+ affectedForegroundPermissions,
+ granted = false,
+ isOneTime = false,
+ foregroundOnly = false,
+ doNotAskAgain = true
+ )
}
}
}
@@ -1067,32 +783,61 @@ 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)
+ groupState.state = STATE_GRANTED
+ reportButtonClickResult(
+ groupState,
+ groupState.affectedPermissions,
+ true,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__PHOTOS_SELECTED
+ )
}
@SuppressLint("NewApi")
@@ -1100,75 +845,127 @@ class GrantPermissionsViewModel(
groupState: GroupState,
affectedForegroundPermissions: List<String>?,
granted: Boolean,
+ foregroundOnly: Boolean,
isOneTime: Boolean,
doNotAskAgain: Boolean
) {
- if (groupState.state != STATE_UNKNOWN) {
+ if (!isStateUnknown(groupState.state)) {
// We already dealt with this group, don't re-grant/re-revoke
return
}
+ val shouldAffectBackgroundPermissions =
+ groupState.bgPermissions.isNotEmpty() && !foregroundOnly
+ val shouldAffectForegroundPermssions = groupState.state != STATE_FG_GRANTED_BG_UNKNOWN
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
- }
- if (groupState.isBackground) {
- grantBackgroundRuntimePermissions(app, groupState.group,
- groupState.affectedPermissions)
- } else {
+ result =
+ if (isOneTime) {
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME
+ } else {
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED
+ }
+ if (shouldAffectBackgroundPermissions) {
+ grantBackgroundRuntimePermissions(
+ app,
+ groupState.group,
+ groupState.affectedPermissions
+ )
+ } else if (shouldAffectForegroundPermssions) {
if (affectedForegroundPermissions == null) {
- grantForegroundRuntimePermissions(app, groupState.group,
- groupState.affectedPermissions, isOneTime)
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ groupState.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 {
- val newGroup = grantForegroundRuntimePermissions(app,
- groupState.group, affectedForegroundPermissions, isOneTime)
+ val newGroup =
+ grantForegroundRuntimePermissions(
+ app,
+ groupState.group,
+ affectedForegroundPermissions,
+ isOneTime
+ )
if (!isOneTime || newGroup.isOneTime) {
- KotlinUtils.setFlagsWhenLocationAccuracyChanged(app, newGroup,
- affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION))
+ KotlinUtils.setFlagsWhenLocationAccuracyChanged(
+ app,
+ newGroup,
+ affectedForegroundPermissions.contains(ACCESS_FINE_LOCATION)
+ )
}
}
}
- groupState.state = STATE_ALLOWED
+ groupState.state = STATE_GRANTED
} else {
- if (groupState.isBackground) {
- revokeBackgroundRuntimePermissions(app, groupState.group,
- userFixed = doNotAskAgain, filterPermissions = groupState.affectedPermissions)
- } else {
- if (affectedForegroundPermissions == null ||
- affectedForegroundPermissions.contains(ACCESS_COARSE_LOCATION)) {
- revokeForegroundRuntimePermissions(app, groupState.group,
+ if (shouldAffectBackgroundPermissions) {
+ revokeBackgroundRuntimePermissions(
+ app,
+ groupState.group,
+ userFixed = doNotAskAgain,
+ filterPermissions = groupState.affectedPermissions
+ )
+ } else if (shouldAffectForegroundPermssions) {
+ 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)
+ val permissionsChanged =
+ if (foregroundOnly) {
+ groupState.fgPermissions
+ } else {
+ groupState.affectedPermissions
+ }
+ reportButtonClickResult(groupState, permissionsChanged, granted, result)
}
- private fun reportButtonClickResult(groupState: GroupState, granted: Boolean, result: Int) {
- reportRequestResult(groupState.affectedPermissions, result)
+ private fun reportButtonClickResult(
+ groupState: GroupState,
+ permissions: Set<String>,
+ granted: Boolean,
+ result: Int
+ ) {
+ reportRequestResult(permissions, result)
// group state has changed, reload liveData
requestInfosLiveData.update()
- PermissionDecisionStorageImpl.recordPermissionDecision(app.applicationContext,
- packageName, groupState.group.permGroupName, granted)
- PermissionChangeStorageImpl.recordPermissionChange(packageName)
+
+ if (SdkLevel.isAtLeastT()) {
+ PermissionDecisionStorageImpl.recordPermissionDecision(
+ app.applicationContext,
+ packageName,
+ groupState.group.permGroupName,
+ granted
+ )
+ PermissionChangeStorageImpl.recordPermissionChange(packageName)
+ }
if (granted) {
startDrivingDecisionReminderServiceIfNecessary(groupState.group.permGroupName)
}
@@ -1183,7 +980,10 @@ class GrantPermissionsViewModel(
return
}
DrivingDecisionReminderService.startServiceIfCurrentlyRestricted(
- Utils.getUserContext(app, user), packageName, permGroupName)
+ Utils.getUserContext(app, user),
+ packageName,
+ permGroupName
+ )
}
private fun getGroupWithPerm(
@@ -1197,30 +997,8 @@ class GrantPermissionsViewModel(
return groupsWithPerm.first()
}
- /**
- * An internal class which represents the state of a current AppPermissionGroup grant request.
- */
- internal class GroupState(
- internal val group: LightAppPermGroup,
- internal val isBackground: Boolean,
- internal val affectedPermissions: MutableList<String> = mutableListOf(),
- 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"
- }
- return "${group.permGroupName} $isBackground $stateStr $affectedPermissions"
- }
- }
-
- private fun reportRequestResult(permissions: List<String>, result: Int) {
- for (perm in permissions) {
- reportRequestResult(perm, result)
- }
+ private fun reportRequestResult(permissions: Collection<String>, result: Int) {
+ permissions.forEach { reportRequestResult(it, result) }
}
/**
@@ -1231,19 +1009,41 @@ 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)
+ )
+ val isPackageRestrictedByEnhancedConfirmation =
+ EnhancedConfirmationStatsLogUtils.isPackageEcmRestricted(
+ app,
+ packageName,
+ packageInfo.uid
+ )
+
+ Log.i(
+ LOG_TAG,
+ "Permission grant result requestId=$sessionId " +
+ "callingUid=${packageInfo.uid} " +
+ "callingPackage=$packageName " +
+ "permission=$permission " +
+ "isImplicit=$isImplicit result=$result " +
+ "isPermissionRationaleShown=$isPermissionRationaleShown" +
+ "isPackageRestrictedByEnhancedConfirmation=" +
+ "$isPackageRestrictedByEnhancedConfirmation"
+ )
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,
+ isPackageRestrictedByEnhancedConfirmation
+ )
}
/**
@@ -1253,17 +1053,17 @@ class GrantPermissionsViewModel(
* @param outState The bundle in which to store state
*/
fun saveInstanceState(outState: Bundle) {
- for ((groupKey, groupState) in groupStates) {
- val (groupName, isBackground) = groupKey
- outState.putInt(getInstanceStateKey(groupName, isBackground), groupState.state)
+ for ((groupName, groupState) in groupStates) {
+ outState.putInt(groupName, 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) {
@@ -1272,33 +1072,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(
+ {
+ groupStates[HEALTH_PERMISSION_GROUP]?.state = STATE_SKIPPED
+ 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)
}
}
@@ -1311,56 +1123,69 @@ 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]?.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()
- }
+ groupStates[groupName]?.state = STATE_SKIPPED
+ // 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) {
+ if (groupStates[READ_MEDIA_VISUAL]?.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
+ )
}
/**
@@ -1371,113 +1196,126 @@ 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) {
+ groupStates[returnGroupName]?.state = STATE_SKIPPED
+ 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) {
+ groupStates[returnGroupName]?.state = STATE_SKIPPED
+ 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)
}
- private fun getInstanceStateKey(groupName: String, isBackground: Boolean): String {
- return "${this::class.java.name}_${groupName}_$isBackground"
+ private fun getGrantBehavior(group: LightAppPermGroup): GrantBehavior {
+ return when (group.permGroupName) {
+ LOCATION -> LocationGrantBehavior
+ HEALTH_PERMISSION_GROUP -> HealthGrantBehavior
+ NOTIFICATIONS -> NotificationGrantBehavior
+ STORAGE,
+ READ_MEDIA_VISUAL,
+ READ_MEDIA_AURAL -> StorageGrantBehavior
+ else -> {
+ if (Utils.hasPermWithBackgroundModeCompat(group)) {
+ BackgroundGrantBehavior
+ } else {
+ BasicGrantBehavior
+ }
+ }
+ }
}
private fun logSettingsInteraction(groupName: String, result: Int) {
- val foregroundGroupState = groupStates[groupName to false]
- val backgroundGroupState = groupStates[groupName to true]
+ val groupState = groupStates[groupName] ?: return
+ val backgroundPerms =
+ groupState.affectedPermissions.filter { it in groupState.group.backgroundPermNames }
+ val foregroundPerms = groupState.affectedPermissions.filter { it !in backgroundPerms }
val deniedPrejudiceInSettings =
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE_IN_SETTINGS
when (result) {
GRANTED_ALWAYS -> {
- if (foregroundGroupState != null) {
- 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(
+ groupState.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)
- }
- if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
+ reportRequestResult(
+ foregroundPerms,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_IN_SETTINGS
+ )
+ if (backgroundPerms.isNotEmpty()) {
+ reportRequestResult(
+ backgroundPerms,
+ 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)
- }
- if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS)
- }
+ reportRequestResult(
+ groupState.affectedPermissions,
+ PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_IN_SETTINGS
+ )
}
DENIED_DO_NOT_ASK_AGAIN -> {
- if (foregroundGroupState != null) {
- reportRequestResult(foregroundGroupState.affectedPermissions,
- deniedPrejudiceInSettings)
- }
- if (backgroundGroupState != null) {
- reportRequestResult(backgroundGroupState.affectedPermissions,
- deniedPrejudiceInSettings)
- }
+ reportRequestResult(groupState.affectedPermissions, deniedPrejudiceInSettings)
}
}
}
- /**
- * Log all permission groups which were requested
- */
+ /** Log all permission groups which were requested */
fun logRequestedPermissionGroups() {
if (groupStates.isEmpty()) {
return
@@ -1491,7 +1329,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
*/
@@ -1507,74 +1345,60 @@ 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.i(
+ 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)
}
+ private fun isStateUnknown(state: Int?): Boolean {
+ return state == null || state == STATE_UNKNOWN || state == STATE_FG_GRANTED_BG_UNKNOWN
+ }
+
companion object {
const val APP_PERMISSION_REQUEST_CODE = 1
const val PHOTO_PICKER_REQUEST_CODE = 2
+ const val ECM_REQUEST_CODE = 3
+ 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_GRANTED = 1
private const val STATE_DENIED = 2
private const val STATE_SKIPPED = 3
- private const val STATE_ALREADY_ALLOWED = 4
-
- /**
- * An enum that represents the type of message which should be shown- foreground,
- * background, upgrade, or no message.
- */
- enum class RequestMessage {
- FG_MESSAGE,
- BG_MESSAGE,
- UPGRADE_MESSAGE,
- NO_MESSAGE,
- FG_FINE_LOCATION_MESSAGE,
- FG_COARSE_LOCATION_MESSAGE,
- STORAGE_SUPERGROUP_MESSAGE_Q_TO_S,
- STORAGE_SUPERGROUP_MESSAGE_PRE_Q,
- MORE_PHOTOS_MESSAGE,
- }
-
- /**
- * Make a copy of a list of permissions that is filtered to remove permissions blocked
- * according to the target SDK level.
- */
- fun getSanitizedPermissionsList(
- permissions: Array<String?>,
- targetSdkVersion: Int
- ): List<String> {
- return permissions
- .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 }
- .filterIsInstance<String>()
- }
+ private const val STATE_FG_GRANTED_BG_UNKNOWN = 4
}
}
@@ -1587,13 +1411,61 @@ class GrantPermissionsViewModel(
class GrantPermissionsViewModelFactory(
private val app: Application,
private val packageName: String,
+ private val deviceId: Int,
private val requestedPermissions: List<String>,
+ private val systemRequestedPermissions: List<String>,
private val sessionId: Long,
private val savedState: Bundle?
) : 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,
+ deviceId,
+ requestedPermissions,
+ systemRequestedPermissions,
+ sessionId,
+ savedState
+ )
+ as T
}
}
+
+enum class Prompt {
+ BASIC, // Allow/Deny
+ ONE_TIME_FG, // Allow in foreground/one time/deny
+ FG_ONLY, // Allow in foreground/deny
+ SETTINGS_LINK_FOR_BG, // Allow in foreground/deny, with link to settings to change background
+ SETTINGS_LINK_WITH_OT, // Same as above, but with a one time button
+ UPGRADE_SETTINGS_LINK, // Keep foreground, with link to settings to grant background
+ OT_UPGRADE_SETTINGS_LINK, // Same as above, but the button is "keep one time"
+ LOCATION_TWO_BUTTON_COARSE_HIGHLIGHT, // Choose coarse/fine, foreground/one time/deny, coarse
+ // button highlighted
+ LOCATION_TWO_BUTTON_FINE_HIGHLIGHT, // Same as above, but fine location highlighted
+ LOCATION_COARSE_ONLY, // Only coarse location, foreground/one time/deny
+ LOCATION_FINE_UPGRADE, // Upgrade coarse to fine, upgrade to fine/ one time/ keep coarse
+ SELECT_PHOTOS, // Select photos/allow all photos/deny
+ SELECT_MORE_PHOTOS, // Select more photos/allow all photos/don't allow more
+ // These next two are for T+ devices, and < T apps. They request the old "storage" group, and
+ // we "grant" it, while actually granting the new visual and audio groups
+ STORAGE_SUPERGROUP_Q_TO_S, // Allow/deny, special message
+ STORAGE_SUPERGROUP_PRE_Q, // Allow/deny, special message (different from above)
+ NO_UI_SETTINGS_REDIRECT, // Send the user directly to permission settings
+ NO_UI_PHOTO_PICKER_REDIRECT, // Send the user directly to the photo picker
+ NO_UI_HEALTH_REDIRECT, // Send the user directly to the Health Connect settings
+ NO_UI_REJECT_THIS_GROUP, // Auto deny this permission group
+ NO_UI_REJECT_ALL_GROUPS, // Auto deny all permission groups in this request
+ NO_UI_FILTER_THIS_GROUP, // Do not act on this permission group. Remove it from results.
+}
+
+enum class DenyButton {
+ DENY,
+ DENY_DONT_ASK_AGAIN,
+ NO_UPGRADE,
+ NO_UPGRADE_OT,
+ NO_UPGRADE_AND_DONT_ASK_AGAIN,
+ NO_UPGRADE_AND_DONT_ASK_AGAIN_OT,
+ DONT_SELECT_MORE, // used in the SELECT_MORE_PHOTOS dialog
+ NONE,
+}
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..9317e7577 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,8 @@ 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) { SensorStatusLiveData() }
fun updateShowSystem(showSystem: Boolean) {
if (showSystem != state.get(SHOULD_SHOW_SYSTEM_KEY)) {
@@ -111,9 +109,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 +153,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 +163,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 +186,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 +211,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 +236,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 +271,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 +284,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 +315,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 +334,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 +389,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 +446,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.i(
+ tag,
+ tag +
+ " created with sessionId=" +
+ sessionId +
+ " permissionGroupName=" +
+ permGroupName +
+ " appUid=" +
+ uid +
+ " packageName=" +
+ packageName +
+ " category=" +
+ category
+ )
}
}
@@ -464,13 +487,15 @@ 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)
- @Suppress("UNCHECKED_CAST")
- return PermissionAppsViewModel(state, app, groupName) as T
+ 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(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 4e1fc1861..8613d1cae 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/grantPermissions/BackgroundGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BackgroundGrantBehavior.kt
new file mode 100644
index 000000000..6234b2755
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BackgroundGrantBehavior.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.grantPermissions
+
+import android.os.Build
+import android.permission.PermissionManager
+import android.util.Log
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+
+/**
+ * This behavior handles groups that respect the difference between foreground and background
+ * access. At present, this includes all one-time permissions, in addition to those with explicit
+ * background permissions.
+ *
+ * The behavior is split based on the target SDK when the app split into foreground and background
+ * (or android R, whichever is newer). If the app targets prior to the split, we show a dialog with
+ * a link to permission settings. Once the app targets after the split, we no longer allow the app
+ * to request background and foreground at the same time. If they try, we reject it. An app has to
+ * request foreground, get it granted, then request background only, we send the user to settings.
+ */
+object BackgroundGrantBehavior : GrantBehavior() {
+
+ private val splitPermissionSdkMap = buildMap {
+ for (splitPerm in
+ PermissionControllerApplication.get()
+ .getSystemService(PermissionManager::class.java)!!
+ .splitPermissions) {
+ put(splitPerm.splitPermission, splitPerm.targetSdk)
+ }
+ }
+
+ // Redundant "if" conditions are suppressed, because the conditions are clearer
+ @Suppress("KotlinConstantConditions")
+ override fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean
+ ): Prompt {
+ val requestsBg = hasBgPerms(group, requestedPerms)
+ val requestsFg = requestedPerms.any { it !in group.backgroundPermNames }
+ val isOneTimeGroup = PermissionMapping.supportsOneTimeGrant(group.permGroupName)
+ val isFgGranted = group.foreground.isGrantedExcludingRWROrAllRWR
+ val isFgOneTime = group.foreground.isOneTime
+ val splitSdk = getSdkGroupWasSplitToBg(requestedPerms)
+ val isAppIsOlderThanSplitToBg = group.packageInfo.targetSdkVersion < splitSdk
+
+ if (!requestsBg && !isOneTimeGroup) {
+ return Prompt.FG_ONLY
+ } else if (!requestsBg) {
+ return Prompt.ONE_TIME_FG
+ }
+
+ if (requestsBg && !requestsFg && !isFgGranted) {
+ Log.w(
+ LOG_TAG,
+ "Cannot grant ${group.permGroupName} as the foreground permissions" +
+ " are not requested or already granted."
+ )
+ return Prompt.NO_UI_REJECT_THIS_GROUP
+ }
+
+ return if (isAppIsOlderThanSplitToBg) {
+ getPromptForOlderApp(isOneTimeGroup, requestsFg, isFgGranted, isFgOneTime)
+ } else {
+ getPromptForNewerApp(group.permGroupName, splitSdk, requestsFg, isFgGranted)
+ }
+ }
+
+ /**
+ * Get the prompt for an app that targets before the sdk level where the permission group was
+ * split into foreground and background.
+ */
+ private fun getPromptForOlderApp(
+ isOneTimeGroup: Boolean,
+ requestsFg: Boolean,
+ isFgGranted: Boolean,
+ isFgOneTime: Boolean
+ ): Prompt {
+ if (requestsFg && !isFgGranted) {
+ if (isOneTimeGroup) {
+ return Prompt.SETTINGS_LINK_WITH_OT
+ }
+ return Prompt.SETTINGS_LINK_FOR_BG
+ }
+
+ if (isFgGranted) {
+ if (isFgOneTime) {
+ return Prompt.OT_UPGRADE_SETTINGS_LINK
+ }
+ return Prompt.UPGRADE_SETTINGS_LINK
+ }
+ return Prompt.NO_UI_REJECT_ALL_GROUPS
+ }
+
+ /**
+ * Get the prompt for an app that targets at least the sdk level where the permission group was
+ * split into foreground and background.
+ */
+ private fun getPromptForNewerApp(
+ groupName: String,
+ splitSdk: Int,
+ requestsFg: Boolean,
+ isFgGranted: Boolean
+ ): Prompt {
+ if (!requestsFg && isFgGranted) {
+ return Prompt.NO_UI_SETTINGS_REDIRECT
+ }
+
+ if (requestsFg) {
+ Log.e(
+ LOG_TAG,
+ "For SDK $splitSdk+ apps requesting $groupName, " +
+ "background permissions must be requested alone after foreground permissions " +
+ "are already granted"
+ )
+ return Prompt.NO_UI_REJECT_ALL_GROUPS
+ } else if (!isFgGranted) {
+ Log.e(
+ LOG_TAG,
+ "For SDK $splitSdk+ apps requesting, $groupName, " +
+ "background permissions must be requested after foreground permissions are " +
+ "already granted"
+ )
+ Prompt.NO_UI_REJECT_THIS_GROUP
+ }
+ return Prompt.NO_UI_REJECT_ALL_GROUPS
+ }
+
+ override fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton {
+ val basicDenyBehavior = BasicGrantBehavior.getDenyButton(group, requestedPerms, prompt)
+ if (prompt == Prompt.UPGRADE_SETTINGS_LINK || prompt == Prompt.OT_UPGRADE_SETTINGS_LINK) {
+ if (basicDenyBehavior == DenyButton.DENY) {
+ return if (prompt == Prompt.UPGRADE_SETTINGS_LINK) {
+ DenyButton.NO_UPGRADE
+ } else {
+ DenyButton.NO_UPGRADE_OT
+ }
+ }
+ return if (prompt == Prompt.UPGRADE_SETTINGS_LINK) {
+ DenyButton.NO_UPGRADE_AND_DONT_ASK_AGAIN
+ } else {
+ DenyButton.NO_UPGRADE_AND_DONT_ASK_AGAIN_OT
+ }
+ }
+ return basicDenyBehavior
+ }
+
+ override fun isGroupFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ return (!hasBgPerms(group, requestedPerms) ||
+ group.background.isGrantedExcludingRWROrAllRWR) &&
+ group.foreground.isGrantedExcludingRWROrAllRWR
+ }
+
+ override fun isForegroundFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ return group.foreground.isGrantedExcludingRWROrAllRWR
+ }
+
+ override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean {
+ if (perm in group.backgroundPermNames) {
+ return group.background.isUserFixed
+ }
+ return group.foreground.isUserFixed
+ }
+
+ private fun hasBgPerms(group: LightAppPermGroup, requestedPerms: Set<String>): Boolean {
+ return requestedPerms.any { it in group.backgroundPermNames }
+ }
+
+ private fun getSdkGroupWasSplitToBg(requestedPerms: Set<String>): Int {
+ val splitSdks = requestedPerms.mapNotNull { perm -> splitPermissionSdkMap[perm] }
+
+ if (splitSdks.isEmpty()) {
+ // If there was no split found, assume the split happened in R. This applies to
+ // Mic and Camera, which technically split in S, but were treated as foreground-only
+ // in R
+ return Build.VERSION_CODES.R
+ }
+ // The background permission request UI changed in R, so if a split happened before R,
+ // treat it as if it happened in R
+ return maxOf(splitSdks.min(), Build.VERSION_CODES.R)
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BasicGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BasicGrantBehavior.kt
new file mode 100644
index 000000000..d73e459be
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/BasicGrantBehavior.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.grantPermissions
+
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+
+/** A basic group. Shows "allow" and "deny", does not allow fixed permissions to be re-requested */
+object BasicGrantBehavior : GrantBehavior() {
+
+ override fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean
+ ): Prompt {
+ return Prompt.BASIC
+ }
+
+ override fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton {
+ if (prompt in noDenyButtonPrompts) {
+ return DenyButton.NONE
+ }
+ if (group.isUserSet) {
+ return DenyButton.DENY_DONT_ASK_AGAIN
+ }
+ return DenyButton.DENY
+ }
+
+ // A list of prompts without any deny behavior
+ private val noDenyButtonPrompts =
+ listOf(
+ Prompt.NO_UI_SETTINGS_REDIRECT,
+ Prompt.NO_UI_PHOTO_PICKER_REDIRECT,
+ Prompt.NO_UI_HEALTH_REDIRECT,
+ Prompt.NO_UI_REJECT_THIS_GROUP,
+ Prompt.NO_UI_REJECT_ALL_GROUPS,
+ Prompt.NO_UI_FILTER_THIS_GROUP
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt
new file mode 100644
index 000000000..3b3619084
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/GrantBehavior.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.grantPermissions
+
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+
+/** The base behavior all grant behavior objects inherit from. */
+abstract class GrantBehavior {
+ val LOG_TAG = "GrantPermissionsViewModel"
+
+ /**
+ * Get the prompt type for the given set of requested permissions
+ *
+ * @param group The LightAppPermGroup representing the state of the app and its permissions
+ * @param requestedPerms The permissions requested by the app, after filtering
+ * @param isSystemTriggeredPrompt Whether the prompt was triggered by the system, instead of by
+ * the app.
+ */
+ abstract fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean = false
+ ): Prompt
+
+ /**
+ * Get the deny button type for the given set of requested permissions. This is separate from
+ * the prompt, because the same prompt can have multiple deny behaviors, based on if the user
+ * has seen it before.
+ *
+ * @param group The LightAppPermGroup representing the state of the app and its permissions
+ * @param requestedPerms The permissions requested by the app, after filtering
+ * @param prompt The prompt determined by the behavior.
+ */
+ abstract fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton
+
+ /**
+ * Whether the group is considered "fully granted". If it is, any remaining permissions in the
+ * group not already granted will be granted.
+ */
+ open fun isGroupFullyGranted(group: LightAppPermGroup, requestedPerms: Set<String>): Boolean {
+ return group.foreground.isGrantedExcludingRWROrAllRWR
+ }
+
+ /**
+ * Whether or not all foreground permissions in the group are granted. Only different in groups
+ * that respect background and foreground permissions.
+ */
+ open fun isForegroundFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ return isGroupFullyGranted(group, requestedPerms)
+ }
+
+ /**
+ * Whether or not the permission should be considered as "user fixed". If it is, it is removed
+ * from the request.
+ */
+ open fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean {
+ return group.isUserFixed
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt
new file mode 100644
index 000000000..8e540701a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/HealthGrantBehavior.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.grantPermissions
+
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+import com.android.permissioncontroller.permission.utils.Utils
+
+/** Health permissions always redirect to the health connect UI. */
+object HealthGrantBehavior : GrantBehavior() {
+ override fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean
+ ): Prompt {
+ return if (Utils.isHealthPermissionUiEnabled()) {
+ Prompt.NO_UI_HEALTH_REDIRECT
+ } else {
+ Prompt.NO_UI_REJECT_THIS_GROUP
+ }
+ }
+
+ override fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton {
+ return DenyButton.NONE
+ }
+
+ override fun isGroupFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ return requestedPerms.all { group.permissions[it]?.isGrantedIncludingAppOp != false }
+ }
+
+ override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean {
+ return group.permissions[perm]?.isUserFixed == true
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt
new file mode 100644
index 000000000..2c5e2b732
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/LocationGrantBehavior.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.grantPermissions
+
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.os.Build
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+
+/**
+ * The Location Grant behavior is the same as the Background group behavior, up until S. After S,
+ * the fine and coarse location permissions were allowed to be granted separately, and this created
+ * a new set of grant dialogs.
+ */
+object LocationGrantBehavior : GrantBehavior() {
+ override fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean
+ ): Prompt {
+ val backgroundPrompt = BackgroundGrantBehavior.getPrompt(group, requestedPerms)
+ val requestsBackground = requestedPerms.any { it in group.backgroundPermNames }
+ val coarseGranted =
+ group.permissions[ACCESS_COARSE_LOCATION]?.isGrantedIncludingAppOp == true
+ return if (!supportsLocationAccuracy(group) || requestsBackground) {
+ backgroundPrompt
+ } else if (requestedPerms.contains(ACCESS_FINE_LOCATION)) {
+ if (coarseGranted) {
+ Prompt.LOCATION_FINE_UPGRADE
+ } else if (isFineLocationHighlighted(group)) {
+ Prompt.LOCATION_TWO_BUTTON_FINE_HIGHLIGHT
+ } else {
+ Prompt.LOCATION_TWO_BUTTON_COARSE_HIGHLIGHT
+ }
+ } else if (requestedPerms.contains(ACCESS_COARSE_LOCATION) && !coarseGranted) {
+ Prompt.LOCATION_COARSE_ONLY
+ } else {
+ backgroundPrompt
+ }
+ }
+
+ override fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton {
+ return BackgroundGrantBehavior.getDenyButton(group, requestedPerms, prompt)
+ }
+
+ override fun isGroupFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ val requestsBackground = requestedPerms.any { it in group.backgroundPermNames }
+ if (!supportsLocationAccuracy(group) || requestsBackground) {
+ return BackgroundGrantBehavior.isGroupFullyGranted(group, requestedPerms)
+ }
+ return isForegroundFullyGranted(group, requestedPerms)
+ }
+
+ override fun isForegroundFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ if (!supportsLocationAccuracy(group)) {
+ return BackgroundGrantBehavior.isForegroundFullyGranted(group, requestedPerms)
+ }
+
+ if (requestedPerms.contains(ACCESS_FINE_LOCATION)) {
+ return group.permissions[ACCESS_FINE_LOCATION]?.isGrantedIncludingAppOp == true
+ }
+
+ return group.foreground.isGrantedExcludingRWROrAllRWR
+ }
+
+ override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean {
+ if (!supportsLocationAccuracy(group) || perm != ACCESS_COARSE_LOCATION) {
+ return BackgroundGrantBehavior.isPermissionFixed(group, perm)
+ }
+
+ // If the location group is user fixed but ACCESS_COARSE_LOCATION is not, then
+ // ACCESS_FINE_LOCATION must be user fixed. In this case ACCESS_COARSE_LOCATION
+ // is still grantable.
+ return group.foreground.isUserFixed && group.permissions[perm]?.isUserFixed == true
+ }
+
+ private fun supportsLocationAccuracy(group: LightAppPermGroup): Boolean {
+ return KotlinUtils.isLocationAccuracyEnabled() &&
+ group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.S
+ }
+
+ private fun isFineLocationHighlighted(group: LightAppPermGroup): Boolean {
+ // 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 coarseLocationPerm = group.allPermissions[ACCESS_COARSE_LOCATION]
+ val fineLocationPerm = group.allPermissions[ACCESS_FINE_LOCATION]
+ return if (
+ coarseLocationPerm?.isSelectedLocationAccuracy == false &&
+ fineLocationPerm?.isSelectedLocationAccuracy == false
+ ) {
+ // default location precision is true, indicates FINE
+ return true
+ } else {
+ fineLocationPerm?.isSelectedLocationAccuracy == true
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/NotificationGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/NotificationGrantBehavior.kt
new file mode 100644
index 000000000..3867e8646
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/NotificationGrantBehavior.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.permissioncontroller.permission.ui.model.grantPermissions
+
+import android.os.Build.VERSION_CODES.TIRAMISU
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+
+/**
+ * The Notification permission behavior is similar to basic, except:
+ *
+ * It can be triggered by the system. If it's system triggered we only show it until the user makes
+ * one decision. We don't make the user show it twice.
+ *
+ * It can't be explicitly requested from apps that don't yet target android T. If they try, we
+ * remove it entirely from the request, do not return a result, and take no action on it.
+ */
+object NotificationGrantBehavior : GrantBehavior() {
+ override fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean
+ ): Prompt {
+ val isPreT = group.packageInfo.targetSdkVersion < TIRAMISU
+ if (!isSystemTriggeredPrompt && isPreT) {
+ // Apps targeting below T cannot manually request Notifications
+ return Prompt.NO_UI_FILTER_THIS_GROUP
+ } else if (isPreT && group.isUserSet) {
+ // If the user has seen the system-triggered prompt once, don't show it again
+ return Prompt.NO_UI_FILTER_THIS_GROUP
+ }
+ return BasicGrantBehavior.getPrompt(group, requestedPerms, isSystemTriggeredPrompt)
+ }
+
+ override fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton {
+ return BasicGrantBehavior.getDenyButton(group, requestedPerms, prompt)
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt
new file mode 100644
index 000000000..a690f5f59
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/model/grantPermissions/StorageGrantBehavior.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.model.grantPermissions
+
+import android.Manifest.permission.ACCESS_MEDIA_LOCATION
+import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+import android.Manifest.permission_group.READ_MEDIA_VISUAL
+import android.Manifest.permission_group.STORAGE
+import android.os.Build
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
+import com.android.permissioncontroller.permission.ui.model.DenyButton
+import com.android.permissioncontroller.permission.ui.model.Prompt
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptEnabled
+import com.android.permissioncontroller.permission.utils.KotlinUtils.isPhotoPickerPromptSupported
+
+/**
+ * Storage split from one group (STORAGE) into two (READ_MEDIA_VISUAL and READ_MEDIA_AURAL) in T.
+ * There are special dialogs to deal with a pre-T app requesting STORAGE on a post-T device. In U,
+ * the READ_MEDIA_VISUAL group was augmented with the option to select photos, which led to several
+ * new dialogs to handle that.
+ */
+object StorageGrantBehavior : GrantBehavior() {
+ override fun getPrompt(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ isSystemTriggeredPrompt: Boolean
+ ): Prompt {
+ val appSupportsSplitStoragePermissions = appSupportsSplitStoragePermissions(group)
+ if (!SdkLevel.isAtLeastT()) {
+ return Prompt.BASIC
+ } else if (appSupportsSplitStoragePermissions && group.permGroupName == STORAGE) {
+ return Prompt.NO_UI_REJECT_THIS_GROUP
+ }
+
+ if (appSupportsSplitStoragePermissions && !shouldShowPhotoPickerPromptForApp(group)) {
+ return Prompt.BASIC
+ }
+
+ if (!appSupportsSplitStoragePermissions) {
+ if (group.packageInfo.targetSdkVersion < Build.VERSION_CODES.Q) {
+ return Prompt.STORAGE_SUPERGROUP_PRE_Q
+ } else {
+ return Prompt.STORAGE_SUPERGROUP_Q_TO_S
+ }
+ }
+
+ // Else, app supports the new photo picker dialog
+ if (requestedPerms.all { it in getPartialGrantPermissions(group) }) {
+ // Do not allow apps to request only READ_MEDIA_VISUAL_USER_SELECTED
+ return Prompt.NO_UI_REJECT_THIS_GROUP
+ }
+
+ val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]
+ if (userSelectedPerm?.isUserFixed == true && userSelectedPerm.isGrantedIncludingAppOp) {
+ return Prompt.NO_UI_PHOTO_PICKER_REDIRECT
+ }
+
+ if (userSelectedPerm?.isGrantedIncludingAppOp == true) {
+ return Prompt.SELECT_MORE_PHOTOS
+ } else {
+ return Prompt.SELECT_PHOTOS
+ }
+ }
+
+ override fun getDenyButton(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>,
+ prompt: Prompt
+ ): DenyButton {
+ if (prompt == Prompt.SELECT_MORE_PHOTOS) {
+ return DenyButton.DONT_SELECT_MORE
+ }
+ return BasicGrantBehavior.getDenyButton(group, requestedPerms, prompt)
+ }
+
+ override fun isGroupFullyGranted(
+ group: LightAppPermGroup,
+ requestedPerms: Set<String>
+ ): Boolean {
+ if (!isPhotoPickerPromptSupported() || group.permGroupName != READ_MEDIA_VISUAL) {
+ return super.isGroupFullyGranted(group, requestedPerms)
+ }
+
+ return group.permissions.values.any {
+ it.name !in getPartialGrantPermissions(group) && it.isGrantedIncludingAppOp
+ }
+ }
+
+ override fun isPermissionFixed(group: LightAppPermGroup, perm: String): Boolean {
+ val userSelectedPerm = group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]
+ if (
+ userSelectedPerm != null &&
+ userSelectedPerm.isGrantedIncludingAppOp &&
+ userSelectedPerm.isUserFixed
+ ) {
+ // If the user selected permission is fixed and granted, we immediately show the
+ // photo picker, rather than filtering
+ return false
+ }
+ return super.isPermissionFixed(group, perm)
+ }
+
+ private fun appSupportsSplitStoragePermissions(group: LightAppPermGroup) =
+ SdkLevel.isAtLeastT() && group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU
+
+ private fun shouldShowPhotoPickerPromptForApp(group: LightAppPermGroup) =
+ isPhotoPickerPromptEnabled() &&
+ group.permGroupName == READ_MEDIA_VISUAL &&
+ (group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ||
+ appSupportsPhotoPicker(group))
+
+ private fun appSupportsPhotoPicker(group: LightAppPermGroup) =
+ group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU &&
+ group.permissions[READ_MEDIA_VISUAL_USER_SELECTED]?.isImplicit == false
+
+ private fun getPartialGrantPermissions(group: LightAppPermGroup): Set<String> {
+ return if (appSupportsPhotoPicker(group) && shouldShowPhotoPickerPromptForApp(group)) {
+ setOf(READ_MEDIA_VISUAL_USER_SELECTED, ACCESS_MEDIA_LOCATION)
+ } else {
+ setOf(READ_MEDIA_VISUAL_USER_SELECTED)
+ }
+ }
+}
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..85f907fd3 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,21 +28,26 @@ 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.location.LocationManager
import android.os.Build
import android.os.Bundle
import android.os.UserHandle
+import android.os.UserManager
+import android.permission.flags.Flags
import androidx.annotation.RequiresApi
+import androidx.annotation.StringRes
import androidx.lifecycle.AbstractSavedStateViewModelFactory
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
import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.data.v31.AllLightHistoricalPackageOpsLiveData
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.model.livedatatypes.v31.AppPermissionId
@@ -54,7 +59,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,8 +84,13 @@ 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)
+ private val userManager =
+ Utils.getSystemServiceSafe(application.applicationContext, UserManager::class.java)
/** Updates whether system app permissions usage should be displayed in the UI. */
fun updateShowSystemAppsToggle(showSystem: Boolean) {
@@ -109,14 +118,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)
+ )
}
/**
@@ -135,8 +149,7 @@ class PermissionUsageDetailsViewModel(
.filterOutExemptAppPermissions(true)
.filterAccessesLaterThan(startTime)
}
- ?.any { isAppPermissionSystem(it.appPermissionId) }
- ?: false
+ ?.any { isAppPermissionSystem(it.appPermissionId) } ?: false
}
private fun isPermissionRequestedByApp(appPermissionId: AppPermissionId): Boolean {
@@ -144,8 +157,15 @@ class PermissionUsageDetailsViewModel(
lightPackageInfoLiveDataMap[
Pair(appPermissionId.packageName, appPermissionId.userHandle)]
?.value
- ?.requestedPermissions
- ?: listOf()
+ ?.requestedPermissions ?: listOf()
+
+ if (
+ appPermissionId.permissionGroup == Manifest.permission_group.LOCATION &&
+ appRequestedPermissions.contains(Manifest.permission.LOCATION_BYPASS)
+ ) {
+ return true
+ }
+
return appRequestedPermissions.any {
PermissionMapping.getGroupOfPlatformPermission(it) == appPermissionId.permissionGroup
}
@@ -162,9 +182,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
@@ -181,10 +203,10 @@ class PermissionUsageDetailsViewModel(
): List<AppPermissionAccessUiInfo> {
return allLightHistoricalPackageOpsLiveData
.getLightHistoricalPackageOps()
+ ?.filter { Utils.shouldShowInSettings(it.userHandle, userManager) }
?.flatMap { it.clusterAccesses(startTime, showSystem) }
?.sortedBy { -1 * it.discreteAccesses.first().accessTimeMs }
- ?.map { it.buildAppPermissionAccessUiInfo() }
- ?: listOf()
+ ?.map { it.buildAppPermissionAccessUiInfo() } ?: listOf()
}
private fun LightHistoricalPackageOps.clusterAccesses(
@@ -244,16 +266,16 @@ 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. */
private fun List<AppPermissionDiscreteAccessesWithLabel>.filterOutExemptAppPermissions(
showSystem: Boolean
): List<AppPermissionDiscreteAccessesWithLabel> {
- return this.filter {
- !Utils.getExemptedPackages(roleManager).contains(it.appPermissionId.packageName)
- }
+ val exemptedPackages = Utils.getExemptedPackages(roleManager)
+ return filter { !exemptedPackages.contains(it.appPermissionId.packageName) }
.filter { it.appPermissionId.permissionGroup == permissionGroup }
.filter { isPermissionRequestedByApp(it.appPermissionId) }
.filter { showSystem || !isAppPermissionSystem(it.appPermissionId) }
@@ -268,7 +290,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 +330,9 @@ class PermissionUsageDetailsViewModel(
appPermissionId,
label,
tags,
- discreteAccesses.sortedBy { -1 * it.accessTimeMs }))
+ discreteAccesses.sortedBy { -1 * it.accessTimeMs }
+ )
+ )
}
return appPermissionDiscreteAccessWithLabels
@@ -334,7 +359,11 @@ class PermissionUsageDetailsViewModel(
appPermAccesses.appPermissionId,
appPermAccesses.attributionLabel,
appPermAccesses.attributionTags,
- currentDiscreteAccesses.toMutableList()))
+ currentDiscreteAccesses.toMutableList(),
+ if (isOpClusteredByItself(discreteAccess.opName)) discreteAccess.opName
+ else null
+ )
+ )
currentDiscreteAccesses.clear()
currentDiscreteAccesses.add(discreteAccess)
} else {
@@ -343,12 +372,16 @@ class PermissionUsageDetailsViewModel(
}
if (currentDiscreteAccesses.isNotEmpty()) {
+ val opName = currentDiscreteAccesses.first().opName
clusters.add(
AppPermissionDiscreteAccessCluster(
appPermAccesses.appPermissionId,
appPermAccesses.attributionLabel,
appPermAccesses.attributionTags,
- currentDiscreteAccesses.toMutableList()))
+ currentDiscreteAccesses.toMutableList(),
+ if (isOpClusteredByItself(opName)) opName else null
+ )
+ )
}
return clusters
}
@@ -360,11 +393,30 @@ class PermissionUsageDetailsViewModel(
private fun canAccessBeAddedToCluster(
discreteAccess: DiscreteAccess,
clusteredAccesses: List<DiscreteAccess>
- ): Boolean =
- discreteAccess.accessTimeMs / ONE_HOUR_MS ==
+ ): Boolean {
+ if (
+ isOpClusteredByItself(discreteAccess.opName) &&
+ discreteAccess.opName != clusteredAccesses.first().opName
+ ) {
+ return false
+ }
+
+ return discreteAccess.accessTimeMs / ONE_HOUR_MS ==
clusteredAccesses.first().accessTimeMs / ONE_HOUR_MS &&
clusteredAccesses.last().accessTimeMs / ONE_MINUTE_MS -
discreteAccess.accessTimeMs / ONE_MINUTE_MS <= CLUSTER_SPACING_MINUTES
+ }
+
+ /**
+ * Determine if an op should be in its own cluster and hence display as an individual entry in
+ * the privacy timeline
+ */
+ private fun isOpClusteredByItself(opName: String): Boolean {
+ if (isLocationByPassEnabled()) {
+ return opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION
+ }
+ return false
+ }
/**
* Composes all UI information from a [AppPermissionDiscreteAccessCluster] into a
@@ -380,16 +432,37 @@ class PermissionUsageDetailsViewModel(
val showingSubAttribution = subAttributionLabel != null && subAttributionLabel.isNotEmpty()
val summary =
buildUsageSummary(context, subAttributionLabel, proxyLabel, durationSummaryLabel)
+ val onClickDialog = getPermissionUsageOnclickDialog(this.clusteredOp)
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),
+ onClickDialog
+ )
+ }
+
+ /**
+ * If an app op is clustered by itself and clicking its access entry in the privacy timeline
+ * displays an alert dialog, return the dialog with title and description.
+ */
+ private fun getPermissionUsageOnclickDialog(
+ clusteredOp: String?
+ ): PermissionUsageOnClickDialog? {
+ if (isLocationByPassEnabled() && clusteredOp == AppOpsManager.OPSTR_EMERGENCY_LOCATION) {
+ val title = R.string.privacy_dashboard_emergency_location_dialog_title
+ val description = R.string.privacy_dashboard_emergency_location_dialog_description
+ return PermissionUsageOnClickDialog(title, description)
+ }
+
+ return null
}
/** Builds a summary of the permission access. */
@@ -410,10 +483,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,18 +539,30 @@ class PermissionUsageDetailsViewModel(
.firstOrNull { it.proxy?.packageName != null }
?.let {
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. */
- private fun getSubAttributionLabel(accessCluster: AppPermissionDiscreteAccessCluster): String? =
- if (accessCluster.attributionLabel == Resources.ID_NULL) null
+ private fun getSubAttributionLabel(accessCluster: AppPermissionDiscreteAccessCluster): String? {
+ // Special case for EMERGENCY_LOCATION app op. Show enforced attribution label in the
+ // Privacy Dashboard
+ if (
+ isLocationByPassEnabled() &&
+ accessCluster.clusteredOp == AppOpsManager.OPSTR_EMERGENCY_LOCATION
+ ) {
+ return application.getString(
+ R.string.privacy_dashboard_emergency_location_enforced_attribution_label
+ )
+ }
+
+ return if (accessCluster.attributionLabel == Resources.ID_NULL) null
else {
val lightPackageInfo = getLightPackageInfo(accessCluster.appPermissionId)
getSubAttributionLabels(lightPackageInfo)?.get(accessCluster.attributionLabel)
}
+ }
private fun getSubAttributionLabels(lightPackageInfo: LightPackageInfo?): Map<Int, String>? =
if (lightPackageInfo == null) null
@@ -492,13 +581,21 @@ 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?,
+ val onClickDialog: PermissionUsageOnClickDialog?
+ )
+
+ data class PermissionUsageOnClickDialog(
+ @StringRes val title: Int,
+ @StringRes val description: Int
)
/**
@@ -535,6 +632,7 @@ class PermissionUsageDetailsViewModel(
val attributionLabel: Int,
val attributionTags: List<String>,
val discreteAccesses: List<DiscreteAccess>,
+ val clusteredOp: String?
)
/**
@@ -584,8 +682,7 @@ class PermissionUsageDetailsViewModel(
?.get(packageWithUserHandle)
?.appPermissionDiscreteAccesses
?.map { it.appPermissionId }
- ?.toSet()
- ?: setOf()
+ ?.toSet() ?: setOf()
appPermissionIds.addAll(appPermGroupIds)
}
@@ -593,13 +690,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 +714,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 +760,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()
@@ -639,6 +771,9 @@ class PermissionUsageDetailsViewModel(
if (SdkLevel.isAtLeastT()) {
add(AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO)
}
+ if (isLocationByPassEnabled()) {
+ add(AppOpsManager.OPSTR_EMERGENCY_LOCATION)
+ }
}
/** Creates the [Intent] for the click action of a privacy dashboard app usage event. */
@@ -659,8 +794,8 @@ class PermissionUsageDetailsViewModel(
accessStartTime,
accessEndTime,
showingAttribution,
- attributionTags)
- ?: getDefaultManageAppPermissionsIntent(packageName, userHandle)
+ attributionTags
+ ) ?: getDefaultManageAppPermissionsIntent(packageName, userHandle)
}
/**
@@ -676,8 +811,14 @@ class PermissionUsageDetailsViewModel(
showingAttribution: Boolean,
attributionTags: List<String>
): Intent? {
- // TODO(b/255992934) only location provider apps should be able to provide this intent
- if (!showingAttribution || !SdkLevel.isAtLeastT()) {
+ if (
+ !showingAttribution ||
+ !SdkLevel.isAtLeastT() ||
+ !context
+ .getSystemService(LocationManager::class.java)!!
+ .isProviderPackage(packageName)
+ ) {
+ // We should only limit this intent to location provider
return null
}
val intent =
@@ -692,11 +833,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)
@@ -712,6 +858,9 @@ class PermissionUsageDetailsViewModel(
putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
}
}
+
+ private fun isLocationByPassEnabled(): Boolean =
+ SdkLevel.isAtLeastV() && Flags.locationBypassPrivacyDashboardEnabled()
}
/** Factory for [PermissionUsageDetailsViewModel]. */
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
deleted file mode 100644
index eeac22124..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/model/v31/PermissionUsageViewModel.kt
+++ /dev/null
@@ -1,377 +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.permissioncontroller.permission.ui.model.v31
-
-import android.Manifest
-import android.app.Application
-import android.app.role.RoleManager
-import android.os.Build
-import android.os.Bundle
-import android.os.UserHandle
-import androidx.annotation.RequiresApi
-import androidx.lifecycle.AbstractSavedStateViewModelFactory
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.SavedStateHandle
-import androidx.lifecycle.ViewModel
-import androidx.savedstate.SavedStateRegistryOwner
-import com.android.permissioncontroller.permission.data.AppPermGroupUiInfoLiveData
-import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
-import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
-import com.android.permissioncontroller.permission.data.StandardPermGroupNamesLiveData
-import com.android.permissioncontroller.permission.data.v31.AllLightPackageOpsLiveData
-import com.android.permissioncontroller.permission.model.livedatatypes.v31.AppPermissionId
-import com.android.permissioncontroller.permission.model.livedatatypes.v31.LightPackageOps
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.Companion.SHOULD_SHOW_SYSTEM_KEY
-import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageViewModel.Companion.SHOULD_SHOW_7_DAYS_KEY
-import com.android.permissioncontroller.permission.utils.KotlinUtils
-import com.android.permissioncontroller.permission.utils.PermissionMapping
-import com.android.permissioncontroller.permission.utils.Utils
-import java.time.Instant
-import java.util.concurrent.TimeUnit
-import kotlin.math.max
-
-/**
- * [ViewModel] for handheld Permissions Usage UI.
- *
- * Note that this class replaces [PermissionUsageViewModelLegacy] to rely on [LiveData] instead of
- * [PermissionUsages] loader.
- */
-class PermissionUsageViewModel(
- private val state: SavedStateHandle,
- app: Application,
-) : AndroidViewModel(app) {
- private val roleManager =
- Utils.getSystemServiceSafe(app.applicationContext, RoleManager::class.java)
- private val exemptedPackages: Set<String> = Utils.getExemptedPackages(roleManager)
-
- private val mAllLightPackageOpsLiveData = AllLightPackageOpsLiveData(app)
- private val appPermGroupUiInfoLiveDataList =
- mutableMapOf<AppPermissionId, AppPermGroupUiInfoLiveData>()
- private val lightPackageInfoLiveDataMap =
- mutableMapOf<Pair<String, UserHandle>, LightPackageInfoLiveData>()
- private val standardPermGroupNamesLiveData = StandardPermGroupNamesLiveData
-
- val showSystemAppsLiveData = state.getLiveData(SHOULD_SHOW_SYSTEM_KEY, false)
- val show7DaysLiveData = state.getLiveData(SHOULD_SHOW_7_DAYS_KEY, false)
-
- /** Updates whether system app permissions usage should be displayed in the UI. */
- fun updateShowSystem(showSystem: Boolean) {
- if (showSystem != state[SHOULD_SHOW_SYSTEM_KEY]) {
- state[SHOULD_SHOW_SYSTEM_KEY] = showSystem
- }
- }
-
- /** Updates whether 7 days usage or 1 day usage should be displayed in the UI. */
- fun updateShow7Days(show7Days: Boolean) {
- if (show7Days != state[SHOULD_SHOW_7_DAYS_KEY]) {
- state[SHOULD_SHOW_7_DAYS_KEY] = show7Days
- }
- }
-
- /** Builds a [PermissionUsagesUiData] containing all the data necessary to render the UI. */
- private fun buildPermissionUsagesUiData(): PermissionUsagesUiData {
- val curTime = System.currentTimeMillis()
- val showSystem: Boolean = state[SHOULD_SHOW_SYSTEM_KEY] ?: false
- val show7Days: Boolean = state[SHOULD_SHOW_7_DAYS_KEY] ?: false
- val showPermissionUsagesDuration =
- if (KotlinUtils.is7DayToggleEnabled() && show7Days) {
- TIME_7_DAYS_DURATION
- } else {
- TIME_24_HOURS_DURATION
- }
- val startTime = max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli())
- return PermissionUsagesUiData(
- showSystem,
- show7Days,
- mAllLightPackageOpsLiveData.containsSystemAppUsages(startTime),
- mAllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(startTime, showSystem))
- }
-
- /** Builds a map of permission groups to the number of apps that recently accessed them. */
- private fun AllLightPackageOpsLiveData.buildPermissionGroupsWithUsageCounts(
- startTime: Long,
- showSystem: Boolean,
- ): Map<String, Int> {
- val permissionUsageCountMap: MutableMap<String, Int> = HashMap()
- for (permissionGroup: String in getAllEligiblePermissionGroups()) {
- permissionUsageCountMap[permissionGroup] = 0
- }
-
- val eligibleLightPackageOpsList: List<LightPackageOps> =
- getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf()
-
- for (lightPackageOps: LightPackageOps in eligibleLightPackageOpsList) {
- val permGroupsToLastAccess: List<Map.Entry<String, Long>> =
- lightPackageOps.lastPermissionGroupAccessTimesMs.entries
- .filterOutExemptedPermissionGroupsFromKeys()
- .filterOutPermissionsNotRequestedByApp(
- lightPackageOps.packageName, lightPackageOps.userHandle)
- .filterOutSystemAppPermissionsIfNecessary(
- showSystem, lightPackageOps.packageName, lightPackageOps.userHandle)
- .filterAccessTimeLaterThan(startTime)
- val recentlyUsedPermissions: List<String> = permGroupsToLastAccess.map { it.key }
-
- for (permissionGroup: String in recentlyUsedPermissions) {
- permissionUsageCountMap[permissionGroup] =
- permissionUsageCountMap.getOrDefault(permissionGroup, 0) + 1
- }
- }
- return permissionUsageCountMap
- }
-
- /**
- * Determines whether there are any system app permissions with recent usage, in which case the
- * "show/hide system" toggle should be displayed in the UI.
- */
- private fun AllLightPackageOpsLiveData.containsSystemAppUsages(startTime: Long): Boolean {
- val eligibleLightPackageOpsList: List<LightPackageOps> =
- getAllLightPackageOps()?.filterOutExemptedApps() ?: listOf()
-
- for (lightPackageOps: LightPackageOps in eligibleLightPackageOpsList) {
- val recentlyUsedPermissions: Set<String> =
- lightPackageOps.lastPermissionGroupAccessTimesMs.entries
- .filterAccessTimeLaterThan(startTime)
- .map { it.key }
- .toSet()
- if (recentlyUsedPermissions
- .filterOutExemptedPermissionGroups()
- .containsSystemAppPermission(
- lightPackageOps.packageName, lightPackageOps.userHandle)) {
- return true
- }
- }
-
- return false
- }
-
- /** Returns all permission groups eligible for display in the UI. */
- private fun getAllEligiblePermissionGroups(): Set<String> =
- standardPermGroupNamesLiveData.value?.filterOutExemptedPermissionGroups()?.toSet()
- ?: setOf()
-
- private fun isPermissionRequestedByApp(appPermissionId: AppPermissionId): Boolean {
- val appRequestedPermissions =
- lightPackageInfoLiveDataMap[
- Pair(appPermissionId.packageName, appPermissionId.userHandle)]
- ?.value
- ?.requestedPermissions
- ?: listOf()
- return appRequestedPermissions.any {
- PermissionMapping.getGroupOfPlatformPermission(it) == appPermissionId.permissionGroup
- }
- }
-
- private fun isAppPermissionSystem(appPermissionId: AppPermissionId): Boolean {
- val appPermGroupUiInfo = appPermGroupUiInfoLiveDataList[appPermissionId]?.value
-
- if (appPermGroupUiInfo != null) {
- return appPermGroupUiInfo.isSystem
- } else
- // The AppPermGroupUiInfo may be null if it has either not loaded yet or if the app has not
- // requested any permissions from the permission group in question.
- // 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)) {
- return true
- }
- return false
- }
-
- private fun AllLightPackageOpsLiveData.getAllLightPackageOps() = value?.values
-
- /**
- * Filters out accesses earlier than the provided start time from a map of permission last
- * accesses.
- */
- private fun Collection<Map.Entry<String, Long>>.filterAccessTimeLaterThan(startTime: Long) =
- filter {
- it.value > startTime
- }
-
- /** Filters out app permissions when the permission has not been requested by the app. */
- private fun Collection<Map.Entry<String, Long>>.filterOutPermissionsNotRequestedByApp(
- packageName: String,
- userHandle: UserHandle
- ) = filter { isPermissionRequestedByApp(AppPermissionId(packageName, userHandle, it.key)) }
-
- /**
- * Filters out system app permissions from a map of permission last accesses, if showSystem is
- * false.
- */
- private fun Collection<Map.Entry<String, Long>>.filterOutSystemAppPermissionsIfNecessary(
- showSystem: Boolean,
- packageName: String,
- userHandle: UserHandle
- ) = filter {
- showSystem || !isAppPermissionSystem(AppPermissionId(packageName, userHandle, it.key))
- }
-
- /**
- * Filters out permission groups that are exempt from permission usage tracking from a map of
- * permission last accesses.
- */
- private fun Collection<Map.Entry<String, Long>>.filterOutExemptedPermissionGroupsFromKeys() =
- filter {
- !EXEMPTED_PERMISSION_GROUPS.contains(it.key)
- }
-
- /**
- * Filters out permission groups that are exempt from permission usage tracking from a map of
- * permission last accesses.
- */
- private fun Collection<String>.filterOutExemptedPermissionGroups() = filter {
- !EXEMPTED_PERMISSION_GROUPS.contains(it)
- }
-
- /** Filters out [LightPackageOps] for apps that are exempt from permission usage tracking. */
- private fun Collection<LightPackageOps>.filterOutExemptedApps() = filter {
- !exemptedPackages.contains(it.packageName)
- }
-
- /**
- * Returns from a list of permissions whether any permission along with the provided package
- * name and user handle are considered a system app permission.
- */
- private fun Collection<String>.containsSystemAppPermission(
- packageName: String,
- userHandle: UserHandle
- ) = any { isAppPermissionSystem(AppPermissionId(packageName, userHandle, it)) }
-
- /** Data class to hold all the information required to configure the UI. */
- data class PermissionUsagesUiData(
- /**
- * Whether to show data over the last 7 days.
- *
- * While this information is available from the [SHOULD_SHOW_7_DAYS_KEY] state, we include
- * it in the UI info so that it triggers a UI update when changed.
- */
- private val show7DaysUsage: Boolean,
- /**
- * Whether to show system apps' data.
- *
- * While this information is available from the [SHOULD_SHOW_SYSTEM_KEY] state, we include
- * it in the UI info so that it triggers a UI update when changed.
- */
- private val showSystem: Boolean,
- /** Whether to show the "show/hide system" toggle. */
- val containsSystemAppUsages: Boolean,
- /** Map instances for display in UI */
- val permissionGroupsWithUsageCount: Map<String, Int>,
- )
-
- /** LiveData object for [PermissionUsagesUiData]. */
- val permissionUsagesUiLiveData =
- object : SmartUpdateMediatorLiveData<@JvmSuppressWildcards PermissionUsagesUiData>() {
- private val getAppPermGroupUiInfoLiveData = { appPermissionId: AppPermissionId ->
- AppPermGroupUiInfoLiveData[
- Triple(
- appPermissionId.packageName,
- appPermissionId.permissionGroup,
- appPermissionId.userHandle,
- )]
- }
- private val getLightPackageInfoLiveData = { packageUser: Pair<String, UserHandle> ->
- LightPackageInfoLiveData[packageUser]
- }
-
- init {
- addSource(mAllLightPackageOpsLiveData) { update() }
- addSource(showSystemAppsLiveData) { update() }
- addSource(show7DaysLiveData) { update() }
- addSource(standardPermGroupNamesLiveData) { update() }
- }
-
- override fun onUpdate() {
- if (mAllLightPackageOpsLiveData.isStale) {
- return
- }
-
- val appPermissionIds = mutableListOf<AppPermissionId>()
- val allPackages = mAllLightPackageOpsLiveData.value?.keys ?: setOf()
- for (packageWithUserHandle: Pair<String, UserHandle> in allPackages) {
- for (permissionGroup in getAllEligiblePermissionGroups()) {
- appPermissionIds.add(
- AppPermissionId(
- packageWithUserHandle.first,
- packageWithUserHandle.second,
- permissionGroup,
- ))
- }
- }
-
- setSourcesToDifference(
- appPermissionIds,
- appPermGroupUiInfoLiveDataList,
- getAppPermGroupUiInfoLiveData) {
- update()
- }
-
- setSourcesToDifference(
- allPackages, lightPackageInfoLiveDataMap, getLightPackageInfoLiveData) {
- update()
- }
-
- if (lightPackageInfoLiveDataMap.any { it.value.isStale }) {
- return
- }
-
-
- if (appPermGroupUiInfoLiveDataList.any { it.value.isStale }) {
- return
- }
-
- val uiData = buildPermissionUsagesUiData()
- // We include this check as we don't want UX updates unless the data to be displayed
- // has changed. SmartUpdateMediatorLiveData sends updates if the data has changed OR
- // if the data has changed from stale to fresh.
- if (value != uiData) {
- value = uiData
- }
- }
- }
-
- /** Companion class for [PermissionUsageViewModel]. */
- companion object {
- private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7)
- private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1)
- internal const val SHOULD_SHOW_SYSTEM_KEY = "showSystem"
- internal const val SHOULD_SHOW_7_DAYS_KEY = "show7Days"
- private const val TELECOM_PACKAGE = "com.android.server.telecom"
-
- /** Permission groups that should be hidden from the permissions usage UI. */
- private val EXEMPTED_PERMISSION_GROUPS = setOf(Manifest.permission_group.NOTIFICATIONS)
- }
-
- /** Factory for [PermissionUsageViewModel]. */
- @RequiresApi(Build.VERSION_CODES.S)
- class PermissionUsageViewModelFactory(
- val app: Application,
- owner: SavedStateRegistryOwner,
- defaultArgs: Bundle
- ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
- override fun <T : ViewModel> create(
- key: String,
- modelClass: Class<T>,
- handle: SavedStateHandle
- ): T {
- @Suppress("UNCHECKED_CAST") return PermissionUsageViewModel(handle, app) as T
- }
- }
-}
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..d3b46defb 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
@@ -51,7 +52,6 @@ import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoing
import com.android.permissioncontroller.permission.ui.handheld.v31.ReviewOngoingUsageFragment.VIDEO_CALL
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.KotlinUtils.shouldShowLocationIndicators
-import com.android.permissioncontroller.permission.utils.KotlinUtils.shouldShowPermissionsDashboard
import com.android.permissioncontroller.permission.utils.Utils
import java.time.Instant
import kotlin.math.max
@@ -62,16 +62,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 +75,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 +98,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 (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 +454,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 +618,16 @@ 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)
- ?: System.currentTimeMillis())
+ 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/AllAppPermissionsFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java
index e5c63f5ab..43cad8335 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/AllAppPermissionsFragment.java
@@ -228,7 +228,7 @@ public final class AllAppPermissionsFragment extends SettingsWithHeader {
if (pref == null) {
pref = new PreferenceCategory(getActivity());
pref.setKey(group.name);
- pref.setLayoutResource(R.layout.preference_category_material);
+ pref.setLayoutResource(androidx.preference.R.layout.preference_category_material);
pref.setTitle(group.loadLabel(pm));
prefs.add(pref);
getPreferenceScreen().addPreference(pref);
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/GrantPermissionsViewHandlerImpl.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
index 45dd4c1f2..da0b28b4d 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
@@ -194,25 +194,19 @@ public final class GrantPermissionsViewHandlerImpl implements GrantPermissionsVi
@Override
public void onClick(View view) {
- switch (view.getId()) {
- case R.id.permission_allow_button:
- mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
- break;
- case R.id.permission_allow_always_button:
- mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
- break;
- case R.id.permission_allow_foreground_only_button:
- mResultListener.onPermissionGrantResult(mGroupName, GRANTED_FOREGROUND_ONLY);
- break;
- case R.id.permission_allow_one_time_button:
- mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ONE_TIME);
- break;
- case R.id.permission_deny_button:
- mResultListener.onPermissionGrantResult(mGroupName, DENIED);
- break;
- case R.id.permission_deny_dont_ask_again_button:
- mResultListener.onPermissionGrantResult(mGroupName, DENIED_DO_NOT_ASK_AGAIN);
- break;
+ final int id = view.getId();
+ if (id == R.id.permission_allow_button) {
+ mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
+ } else if (id == R.id.permission_allow_always_button) {
+ mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
+ } else if (id == R.id.permission_allow_foreground_only_button) {
+ mResultListener.onPermissionGrantResult(mGroupName, GRANTED_FOREGROUND_ONLY);
+ } else if (id == R.id.permission_allow_one_time_button) {
+ mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ONE_TIME);
+ } else if (id == R.id.permission_deny_button) {
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED);
+ } else if (id == R.id.permission_deny_dont_ask_again_button) {
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED_DO_NOT_ASK_AGAIN);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionsFrameFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionsFrameFragment.java
index b3a473914..8a4638839 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionsFrameFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/television/PermissionsFrameFragment.java
@@ -140,7 +140,7 @@ public abstract class PermissionsFrameFragment extends PreferenceFragmentCompat
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
VerticalGridView verticalGridView = (VerticalGridView) inflater.inflate(
- R.layout.leanback_preferences_list, parent, false);
+ androidx.leanback.preference.R.layout.leanback_preferences_list, parent, false);
verticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
verticalGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
mGridView = verticalGridView;
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/ui/viewmodel/v31/PermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/PermissionUsageViewModel.kt
new file mode 100644
index 000000000..2741a3cd8
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/viewmodel/v31/PermissionUsageViewModel.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.viewmodel.v31
+
+import android.app.Application
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import androidx.annotation.RequiresApi
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.AbstractSavedStateViewModelFactory
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
+import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel
+import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageUseCase
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+import kotlin.concurrent.Volatile
+import kotlin.math.max
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.runBlocking
+
+/** Privacy dashboard's new implementation. */
+class PermissionUsageViewModel(
+ val app: Application,
+ private val permissionRepository: PermissionRepository,
+ private val getPermissionUsageUseCase: GetPermissionGroupUsageUseCase,
+ scope: CoroutineScope? = null,
+ private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
+ // Inject the parameter to prevent READ_DEVICE_CONFIG permission error on T- platforms.
+ private val is7DayToggleEnabled: Boolean = KotlinUtils.is7DayToggleEnabled(),
+) : AndroidViewModel(app) {
+ private var showSystemApps = false
+ private var show7DaysData = false
+ private val coroutineScope = scope ?: viewModelScope
+
+ // Cache permission usages to calculate ui state for "show system" and "show 7 days" toggle.
+ @Volatile private var permissionGroupUsages = emptyList<PermissionGroupUsageModel>()
+
+ private val permissionUsagesUiStateFlow: StateFlow<PermissionUsagesUiState> by lazy {
+ getPermissionUsageUseCase()
+ .map { permGroupUsages ->
+ permissionGroupUsages = permGroupUsages
+ buildPermissionUsagesUiState(permGroupUsages)
+ }
+ .flowOn(defaultDispatcher)
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(5000),
+ PermissionUsagesUiState.Loading
+ )
+ }
+
+ val permissionUsagesUiLiveData =
+ permissionUsagesUiStateFlow.asLiveData(context = coroutineScope.coroutineContext)
+
+ @VisibleForTesting
+ fun getPermissionUsagesUiDataFlow(): Flow<PermissionUsagesUiState> {
+ return permissionUsagesUiStateFlow
+ }
+
+ /** Get start time based on whether to show 24 hours or 7 days data. */
+ private fun getStartTime(show7DaysData: Boolean): Long {
+ val curTime = System.currentTimeMillis()
+ val showPermissionUsagesDuration =
+ if (is7DayToggleEnabled && show7DaysData) {
+ TIME_7_DAYS_DURATION
+ } else {
+ TIME_24_HOURS_DURATION
+ }
+ return max(curTime - showPermissionUsagesDuration, Instant.EPOCH.toEpochMilli())
+ }
+
+ /** Builds a [PermissionUsagesUiState] containing all data necessary to render the UI. */
+ private fun buildPermissionUsagesUiState(
+ permissionGroupOps: List<PermissionGroupUsageModel>
+ ): PermissionUsagesUiState {
+ val startTime = getStartTime(show7DaysData)
+ val dashboardPermissionGroups =
+ permissionRepository.getPermissionGroupsForPrivacyDashboard()
+ val permissionUsageCountMap = HashMap<String, Int>(dashboardPermissionGroups.size)
+ for (permissionGroup in dashboardPermissionGroups) {
+ permissionUsageCountMap[permissionGroup] = 0
+ }
+
+ val permGroupOps = permissionGroupOps.filter { it.lastAccessTimestampMillis > startTime }
+ permGroupOps
+ .filter { showSystemApps || it.isUserSensitive }
+ .forEach {
+ permissionUsageCountMap[it.permissionGroup] =
+ permissionUsageCountMap.getOrDefault(it.permissionGroup, 0) + 1
+ }
+ return PermissionUsagesUiState.Success(
+ permGroupOps.any { !it.isUserSensitive },
+ permissionUsageCountMap
+ )
+ }
+
+ fun getShowSystemApps(): Boolean {
+ return showSystemApps
+ }
+
+ fun getShow7DaysData(): Boolean {
+ return show7DaysData
+ }
+
+ fun updateShowSystem(showSystem: Boolean): PermissionUsagesUiState {
+ showSystemApps = showSystem
+ return buildPermissionUsagesUiState(permissionGroupUsages)
+ }
+
+ fun updateShow7Days(show7Days: Boolean): PermissionUsagesUiState {
+ show7DaysData = show7Days
+ return buildPermissionUsagesUiState(permissionGroupUsages)
+ }
+
+ private val permissionGroupLabels = mutableMapOf<String, String>()
+
+ fun getPermissionGroupLabel(context: Context, permissionGroup: String): String {
+ return runBlocking(coroutineScope.coroutineContext + Dispatchers.Default) {
+ permissionGroupLabels.getOrDefault(
+ permissionGroup,
+ permissionRepository.getPermissionGroupLabel(context, permissionGroup).toString()
+ )
+ }
+ }
+
+ /** Companion class for [PermissionUsageViewModel]. */
+ companion object {
+ private val TIME_7_DAYS_DURATION = TimeUnit.DAYS.toMillis(7)
+ private val TIME_24_HOURS_DURATION = TimeUnit.DAYS.toMillis(1)
+ }
+}
+
+/** Data class to hold all the information required to configure the UI. */
+sealed class PermissionUsagesUiState {
+ data object Loading : PermissionUsagesUiState()
+ data class Success(
+ val shouldShowSystemToggle: Boolean,
+ val permissionGroupUsageCount: Map<String, Int>
+ ) : PermissionUsagesUiState()
+}
+
+/** Factory for [PermissionUsageViewModel]. */
+@RequiresApi(Build.VERSION_CODES.S)
+class PermissionUsageViewModelFactory(
+ private val app: Application,
+ owner: SavedStateRegistryOwner,
+ defaultArgs: Bundle
+) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : ViewModel> create(
+ key: String,
+ modelClass: Class<T>,
+ handle: SavedStateHandle
+ ): T {
+ val permissionRepository = PermissionRepository.getInstance(app)
+ val permissionUsageUseCase = GetPermissionGroupUsageUseCase.create(app)
+ return PermissionUsageViewModel(app, permissionRepository, permissionUsageUseCase) as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/AppPermissionsFragmentWear.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/AppPermissionsFragmentWear.java
deleted file mode 100644
index 843514b4a..000000000
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/AppPermissionsFragmentWear.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
-* Copyright (C) 2015 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.permissioncontroller.permission.ui.wear;
-
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.ArraySet;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-import androidx.fragment.app.Fragment;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
-import androidx.preference.SwitchPreference;
-import androidx.wear.ble.view.WearableDialogHelper;
-
-import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.permission.model.AppPermissionGroup;
-import com.android.permissioncontroller.permission.model.AppPermissions;
-import com.android.permissioncontroller.permission.model.Permission;
-import com.android.permissioncontroller.permission.utils.ArrayUtils;
-import com.android.permissioncontroller.permission.utils.LocationUtils;
-import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.permission.utils.legacy.LegacySafetyNetLogger;
-import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public final class AppPermissionsFragmentWear extends PreferenceFragmentCompat {
- private static final String LOG_TAG = "AppPermFragWear";
-
- private static final String KEY_NO_PERMISSIONS = "no_permissions";
-
- public static AppPermissionsFragmentWear newInstance(String packageName) {
- return setPackageName(new AppPermissionsFragmentWear(), packageName);
- }
-
- private static <T extends Fragment> T setPackageName(T fragment, String packageName) {
- Bundle arguments = new Bundle();
- arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
- fragment.setArguments(arguments);
- return fragment;
- }
-
- private PackageManager mPackageManager;
- private ArraySet<AppPermissionGroup> mToggledGroups;
- private AppPermissions mAppPermissions;
-
- private boolean mHasConfirmedRevoke;
-
- /**
- * Provides click behavior for disabled preferences.
- */
- private static class PermissionSwitchPreference extends SwitchPreference {
-
- private final Activity mActivity;
-
- public PermissionSwitchPreference(Activity activity) {
- super(activity);
- this.mActivity = activity;
- }
-
- @Override
- protected void performClick(View view) {
- super.performClick(view);
-
- if (!isEnabled()) {
- // If setting the permission is disabled, it must have been locked
- // by the device or profile owner. So get that info and pass it to
- // the support details dialog.
- EnforcedAdmin deviceOrProfileOwner = RestrictedLockUtils.getProfileOrDeviceOwner(
- mActivity, UserHandle.of(UserHandle.myUserId()));
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
- mActivity, deviceOrProfileOwner);
- }
- }
- }
-
- @Override
- public void onCreatePreferences(Bundle bundle, String s) {
- // empty
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
- Activity activity = getActivity();
- mPackageManager = activity.getPackageManager();
-
- PackageInfo packageInfo;
-
- try {
- packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
- } catch (PackageManager.NameNotFoundException e) {
- Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
- packageInfo = null;
- }
-
- if (packageInfo == null) {
- Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
- activity.finish();
- return;
- }
-
- mAppPermissions = new AppPermissions(
- activity, packageInfo, true, () -> getActivity().finish());
-
- addPreferencesFromResource(R.xml.watch_permissions);
- initializePermissionGroupList();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mAppPermissions.refresh();
-
- // Also refresh the UI
- for (final AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
- if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
- for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
- setPreferenceCheckedIfPresent(perm.name,
- group.areRuntimePermissionsGranted(new String[]{ perm.name }));
- }
- } else {
- setPreferenceCheckedIfPresent(group.getName(), group.areRuntimePermissionsGranted());
- }
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- logAndClearToggledGroups();
- }
-
- private void initializePermissionGroupList() {
- List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups();
- List<SwitchPreference> nonSystemPreferences = new ArrayList<>();
-
- if (!groups.isEmpty()) {
- getPreferenceScreen().removePreference(findPreference(KEY_NO_PERMISSIONS));
- }
-
- for (final AppPermissionGroup group : groups) {
- if (!Utils.shouldShowPermission(getContext(), group)) {
- continue;
- }
-
- boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
-
- if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
- // If permission is controlled individually, we show all requested permission
- // inside this group.
- for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
- final SwitchPreference pref = createSwitchPreferenceForPermission(group, perm);
- showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
- }
- } else {
- final SwitchPreference pref = createSwitchPreferenceForGroup(group);
- showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
- }
- }
-
- // Now add the non-system settings to the end of the list
- for (SwitchPreference nonSystemPreference : nonSystemPreferences) {
- getPreferenceScreen().addPreference(nonSystemPreference);
- }
- }
-
- private void showOrAddToNonSystemPreferences(SwitchPreference pref,
- List<SwitchPreference> nonSystemPreferences, // Mutate
- boolean isPlatform) {
- // The UI shows System settings first, then non-system settings
- if (isPlatform) {
- getPreferenceScreen().addPreference(pref);
- } else {
- nonSystemPreferences.add(pref);
- }
- }
-
- private SwitchPreference createSwitchPreferenceForPermission(AppPermissionGroup group,
- PermissionInfo perm) {
- final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
- pref.setKey(perm.name);
- pref.setTitle(perm.loadLabel(mPackageManager));
- pref.setChecked(group.areRuntimePermissionsGranted(new String[]{ perm.name }));
- pref.setOnPreferenceChangeListener((p, newVal) -> {
- if((Boolean) newVal) {
- group.grantRuntimePermissions(true, false, new String[]{ perm.name });
-
- if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())
- && group.doesSupportRuntimePermissions()) {
- // We are granting a permission from a group but since this is an
- // individual permission control other permissions in the group may
- // be revoked, hence we need to mark them user fixed to prevent the
- // app from requesting a non-granted permission and it being granted
- // because another permission in the group is granted. This applies
- // only to apps that support runtime permissions.
- String[] revokedPermissionsToFix = null;
- final int permissionCount = group.getPermissions().size();
-
- for (int i = 0; i < permissionCount; i++) {
- Permission current = group.getPermissions().get(i);
- if (!current.isGranted() && !current.isUserFixed()) {
- revokedPermissionsToFix = ArrayUtils.appendString(
- revokedPermissionsToFix, current.getName());
- }
- }
-
- if (revokedPermissionsToFix != null) {
- // If some permissions were not granted then they should be fixed.
- group.revokeRuntimePermissions(true, revokedPermissionsToFix);
- }
- }
- } else {
- final Permission appPerm = getPermissionFromGroup(group, perm.name);
- if (appPerm == null) {
- return false;
- }
-
- final boolean grantedByDefault = appPerm.isGrantedByDefault();
- if (grantedByDefault
- || (!group.doesSupportRuntimePermissions() && !mHasConfirmedRevoke)) {
- showRevocationWarningDialog(
- (dialog, which) -> {
- revokePermissionInGroup(group, perm.name);
- pref.setChecked(false);
- if (!appPerm.isGrantedByDefault()) {
- mHasConfirmedRevoke = true;
- }
- },
- grantedByDefault
- ? R.string.system_warning
- : R.string.old_sdk_deny_warning);
- return false;
- } else {
- revokePermissionInGroup(group, perm.name);
- }
- }
-
- return true;
- });
- return pref;
- }
-
- private void showRevocationWarningDialog(
- DialogInterface.OnClickListener confirmListener,
- int warningMessageId) {
- new WearableDialogHelper.DialogBuilder(getContext())
- .setNegativeIcon(R.drawable.confirm_button)
- .setPositiveIcon(R.drawable.cancel_button)
- .setNegativeButton(R.string.grant_dialog_button_deny_anyway, confirmListener)
- .setPositiveButton(R.string.cancel, null)
- .setMessage(warningMessageId)
- .show();
- }
-
- private static Permission getPermissionFromGroup(AppPermissionGroup group, String permName) {
- final int permissionCount = group.getPermissions().size();
-
- for (int i = 0; i < permissionCount; i++) {
- Permission currentPerm = group.getPermissions().get(i);
- if(currentPerm.getName().equals(permName)) {
- return currentPerm;
- };
- }
-
- if ("user".equals(Build.TYPE)) {
- Log.e(LOG_TAG, String.format("The impossible happens, permission %s is not in group %s.",
- permName, group.getName()));
- return null;
- } else {
- // This is impossible, throw a fatal error in non-user build.
- throw new IllegalArgumentException(
- String.format("Permission %s is not in group %s", permName, group.getName()));
- }
- }
-
- private void revokePermissionInGroup(AppPermissionGroup group, String permName) {
- group.revokeRuntimePermissions(true, new String[]{ permName });
-
- if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())
- && group.doesSupportRuntimePermissions()
- && !group.areRuntimePermissionsGranted()) {
- // If we just revoked the last permission we need to clear
- // the user fixed state as now the app should be able to
- // request them at runtime if supported.
- group.revokeRuntimePermissions(false);
- }
- }
-
- private SwitchPreference createSwitchPreferenceForGroup(AppPermissionGroup group) {
- final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
-
- pref.setKey(group.getName());
- pref.setTitle(group.getLabel());
- pref.setChecked(group.areRuntimePermissionsGranted());
-
- if (group.isSystemFixed() || group.isPolicyFixed()) {
- pref.setEnabled(false);
- } else {
- pref.setOnPreferenceChangeListener((p, newVal) -> {
- if (LocationUtils.isLocationGroupAndProvider(getContext(),
- group.getName(), group.getApp().packageName)) {
- LocationUtils.showLocationDialog(
- getContext(), mAppPermissions.getAppLabel());
- return false;
- }
-
- if ((Boolean) newVal) {
- setPermission(group, pref, true);
- } else {
- final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
- if (grantedByDefault
- || (!group.doesSupportRuntimePermissions() && !mHasConfirmedRevoke)) {
- showRevocationWarningDialog(
- (dialog, which) -> {
- setPermission(group, pref, false);
- if (!group.hasGrantedByDefaultPermission()) {
- mHasConfirmedRevoke = true;
- }
- },
- grantedByDefault
- ? R.string.system_warning
- : R.string.old_sdk_deny_warning);
- return false;
- } else {
- setPermission(group, pref, false);
- }
- }
-
- return true;
- });
- }
- return pref;
- }
-
- private void setPermission(AppPermissionGroup group, SwitchPreference pref, boolean grant) {
- if (grant) {
- group.grantRuntimePermissions(true, false);
- } else {
- group.revokeRuntimePermissions(false);
- }
- addToggledGroup(group);
- pref.setChecked(grant);
- }
-
- private void addToggledGroup(AppPermissionGroup group) {
- if (mToggledGroups == null) {
- mToggledGroups = new ArraySet<>();
- }
-
- mToggledGroups.add(group);
- }
-
- private void logAndClearToggledGroups() {
- if (mToggledGroups != null) {
- LegacySafetyNetLogger.logPermissionsToggled(mToggledGroups);
- mToggledGroups = null;
- }
- }
-
- private List<PermissionInfo> getPermissionInfosFromGroup(AppPermissionGroup group) {
- ArrayList<PermissionInfo> permInfos = new ArrayList<>(group.getPermissions().size());
- for(Permission perm : group.getPermissions()) {
- try {
- permInfos.add(mPackageManager.getPermissionInfo(perm.getName(), 0));
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(LOG_TAG, "No permission:" + perm.getName());
- }
- }
- return permInfos;
- }
-
- private void setPreferenceCheckedIfPresent(String preferenceKey, boolean checked) {
- Preference pref = findPreference(preferenceKey);
- if (pref instanceof SwitchPreference) {
- ((SwitchPreference) pref).setChecked(checked);
- }
- }
-}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
index ccd1ed42e..c9e9a2eb1 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/GrantPermissionsWearViewHandler.java
@@ -16,62 +16,99 @@
package com.android.permissioncontroller.permission.ui.wear;
-import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON;
+import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON;
+
+import android.app.Activity;
import android.graphics.drawable.Icon;
import android.os.Bundle;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
-import android.util.Log;
+import android.util.SparseIntArray;
import android.view.View;
-import android.view.WindowManager;
-import android.widget.Space;
+import android.view.WindowManager.LayoutParams;
-import androidx.wear.ble.view.AcceptDenyDialog;
-import androidx.wear.ble.view.WearableDialogHelper;
+import androidx.annotation.Nullable;
+import androidx.compose.ui.platform.ComposeView;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler;
+import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModel;
+import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModelFactory;
+
+import kotlin.Unit;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
- * Watch-specific view handler for the grant permissions activity.
+ * Wear-specific view handler for the grant permissions activity.
*/
-public final class GrantPermissionsWearViewHandler implements GrantPermissionsViewHandler,
- DialogInterface.OnClickListener {
- private static final String TAG = "GrantPermsWatchViewH";
-
- private static final String WATCH_HANDLER_BUNDLE = "watch_handler_bundle";
- private static final String DIALOG_BUNDLE = "dialog_bundle";
- private static final String GROUP_NAME = "group_name";
- private static final String SHOW_DO_NOT_ASK = "show_do_not_ask";
- private static final String ICON = "icon";
- private static final String MESSAGE = "message";
- private static final String CURRENT_PAGE_TEXT = "current_page_text";
+public class GrantPermissionsWearViewHandler implements GrantPermissionsViewHandler {
+
+ public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
+ public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT";
+ public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX";
+ public static final String ARG_GROUP_ICON = "ARG_GROUP_ICON";
+ public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE";
+ private static final String ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE";
+ private static final String ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES";
+ private static final String ARG_DIALOG_LOCATION_VISIBILITIES =
+ "ARG_DIALOG_LOCATION_VISIBILITIES";
+
+ public static final SparseIntArray BUTTON_RES_ID_TO_NUM = new SparseIntArray();
+ static {
+ BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_button, ALLOW_BUTTON);
+ BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_always_button,
+ ALLOW_ALWAYS_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);
+ }
- private final Context mContext;
+ private final Activity mActivity;
private ResultListener mResultListener;
- private Dialog mDialog;
-
+ // Configuration of the current dialog
private String mGroupName;
- private boolean mShowDoNotAsk;
-
- private CharSequence mMessage;
- private String mCurrentPageText;
- private Icon mIcon;
-
- public GrantPermissionsWearViewHandler(Context context) {
- mContext = context;
+ private int mGroupCount;
+ private int mGroupIndex;
+ private Icon mGroupIcon;
+ private CharSequence mGroupMessage;
+ private CharSequence mDetailMessage;
+ private final boolean[] mButtonVisibilities = new boolean[NEXT_BUTTON];
+ private final boolean[] mLocationVisibilities = new boolean[NEXT_LOCATION_DIALOG];
+ // View model to update WearGrantPermissionsScreen elements
+ private WearGrantPermissionsViewModel mViewModel;
+
+ public GrantPermissionsWearViewHandler(Activity activity) {
+ mActivity = activity;
}
@Override
@@ -81,17 +118,30 @@ public final class GrantPermissionsWearViewHandler implements GrantPermissionsVi
}
@Override
- public View createView() {
- return new Space(mContext);
+ public void saveInstanceState(Bundle arguments) {
+ arguments.putString(ARG_GROUP_NAME, mGroupName);
+ arguments.putInt(ARG_GROUP_COUNT, mGroupCount);
+ arguments.putInt(ARG_GROUP_INDEX, mGroupIndex);
+ arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon);
+ arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage);
+ arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, mDetailMessage);
+ arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, mButtonVisibilities);
+ arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, mLocationVisibilities);
}
@Override
- public void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams) {
- outLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
- outLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
- outLayoutParams.format = PixelFormat.OPAQUE;
- outLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
- outLayoutParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ public void loadInstanceState(Bundle savedInstanceState) {
+ mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
+ mGroupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE);
+ mGroupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON);
+ mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT);
+ mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX);
+ mDetailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE);
+ setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES));
+ setLocationVisibilities(
+ savedInstanceState.getBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES));
+
+ updateScreen();
}
@Override
@@ -100,140 +150,167 @@ public final class GrantPermissionsWearViewHandler implements GrantPermissionsVi
CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
boolean[] locationVisibilities) {
// permissionRationaleMessage ignored by wear
-
- // TODO: Handle detailMessage
-
- boolean showDoNotAsk = buttonVisibilities[DENY_AND_DONT_ASK_AGAIN_BUTTON];
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "updateUi() - groupName: " + groupName
- + ", groupCount: " + groupCount
- + ", groupIndex: " + groupIndex
- + ", icon: " + icon
- + ", message: " + message
- + ", showDoNotAsk: " + showDoNotAsk);
+ if (mActivity.isFinishing()) {
+ return;
}
-
mGroupName = groupName;
- mShowDoNotAsk = showDoNotAsk;
- mMessage = message;
- mIcon = icon;
- mCurrentPageText = groupCount > 1
- ? mContext.getString(R.string.current_permission_template,
- groupIndex + 1, groupCount)
- : null;
- showDialog(null);
+ mGroupCount = groupCount;
+ mGroupIndex = groupIndex;
+ mGroupIcon = icon;
+ mGroupMessage = message;
+ mDetailMessage = detailMessage;
+ setButtonVisibilities(buttonVisibilities);
+ setLocationVisibilities(locationVisibilities);
+
+ updateScreen();
}
- private void showDialog(Bundle savedInstanceState) {
- TypedArray a = mContext.obtainStyledAttributes(
- new int[] { android.R.attr.textColorPrimary });
- int color = a.getColor(0, mContext.getColor(android.R.color.white));
- a.recycle();
- Drawable drawable = mIcon == null ? null : mIcon.setTint(color).loadDrawable(mContext);
-
- SpannableStringBuilder ssb = new SpannableStringBuilder();
- if (!TextUtils.isEmpty(mCurrentPageText)) {
- ssb.append(mCurrentPageText, new TextAppearanceSpan(mContext, R.style.BreadcrumbText),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- ssb.append('\n');
+ private void updateScreen() {
+ mViewModel.getIconLiveData().setValue(
+ mGroupIcon == null ? null : mGroupIcon.loadDrawable(mActivity));
+ mViewModel.getGroupMessageLiveData().setValue(
+ mGroupMessage == null ? "" : mGroupMessage.toString());
+ mViewModel.getDetailMessageLiveData().setValue(mDetailMessage);
+ int numButtons = BUTTON_RES_ID_TO_NUM.size();
+ List<Boolean> buttonVisibilityList = Arrays.asList(new Boolean[NEXT_BUTTON]);
+ for (int i = 0; i < numButtons; i++) {
+ int pos = BUTTON_RES_ID_TO_NUM.valueAt(i);
+ buttonVisibilityList.set(pos, mButtonVisibilities[pos] ? Boolean.TRUE : Boolean.FALSE);
}
- if (!TextUtils.isEmpty(mMessage)) {
- ssb.append(mMessage, new TextAppearanceSpan(mContext, R.style.TitleText),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ mViewModel.getButtonVisibilitiesLiveData().setValue(buttonVisibilityList);
+ List<Boolean> locationVisibilityList = Arrays.asList(new Boolean[NEXT_LOCATION_DIALOG]);
+ for (int i = 0; i < locationVisibilityList.size(); i++) {
+ locationVisibilityList.set(i, mLocationVisibilities[i] ? Boolean.TRUE : Boolean.FALSE);
}
-
- if (mDialog != null) {
- mDialog.dismiss();
- mDialog = null;
+ mViewModel.getLocationVisibilitiesLiveData().setValue(locationVisibilityList);
+ if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
+ mViewModel.getPreciseLocationCheckedLiveData()
+ .setValue(mLocationVisibilities[FINE_RADIO_BUTTON]);
+ } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]
+ || mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
+ mViewModel.getPreciseLocationCheckedLiveData().setValue(false);
}
+ }
- if (mShowDoNotAsk) {
- AlertDialog alertDialog = new WearableDialogHelper.DialogBuilder(mContext)
- .setPositiveIcon(R.drawable.confirm_button)
- .setNeutralIcon(R.drawable.cancel_button)
- .setNegativeIcon(R.drawable.deny_button)
- .setTitle(ssb)
- .setIcon(drawable)
- .setPositiveButton(R.string.grant_dialog_button_allow, this)
- .setNeutralButton(R.string.grant_dialog_button_deny, this)
- .setNegativeButton(R.string.grant_dialog_button_deny_dont_ask_again, this)
- .show();
- alertDialog.getButton(DialogInterface.BUTTON_POSITIVE)
- .setId(R.id.permission_allow_button);
- alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL)
- .setId(R.id.permission_deny_button);
- alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE)
- .setId(R.id.permission_deny_dont_ask_again_button);
-
- mDialog = alertDialog;
- } else {
- AcceptDenyDialog acceptDenyDialog = new AcceptDenyDialog(mContext);
- acceptDenyDialog.setTitle(ssb);
- acceptDenyDialog.setIcon(drawable);
- acceptDenyDialog.setPositiveButton(this);
- acceptDenyDialog.setNegativeButton(this);
- acceptDenyDialog.show();
- acceptDenyDialog.getButton(DialogInterface.BUTTON_POSITIVE)
- .setId(R.id.permission_allow_button);
- acceptDenyDialog.getButton(DialogInterface.BUTTON_NEGATIVE)
- .setId(R.id.permission_deny_button);
-
- mDialog = acceptDenyDialog;
+ private void onLocationSwitchChanged(boolean checked) {
+ mViewModel.getPreciseLocationCheckedLiveData().setValue(checked);
+ }
+
+ private void onButtonClicked(int id) {
+ List<String> affectedForegroundPermissions = null;
+ if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
+ if (Boolean.TRUE.equals(mViewModel.getPreciseLocationCheckedLiveData().getValue())) {
+ affectedForegroundPermissions = Arrays.asList(
+ ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION);
+ } else {
+ affectedForegroundPermissions = Collections.singletonList(ACCESS_COARSE_LOCATION);
+ }
+ } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
+ affectedForegroundPermissions = Collections.singletonList(ACCESS_FINE_LOCATION);
+ } else if (mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
+ affectedForegroundPermissions = Collections.singletonList(ACCESS_COARSE_LOCATION);
}
- mDialog.setCancelable(false);
- if (savedInstanceState != null) {
- mDialog.onRestoreInstanceState(savedInstanceState);
+ int button = -1;
+ try {
+ button = BUTTON_RES_ID_TO_NUM.get(id);
+ } catch (NullPointerException e) {
+ // Clicked a view which is not one of the defined buttons
+ return;
+ }
+ // TODO (b/297534305): Research on performAccessibilityAction().
+ switch (button) {
+ case ALLOW_BUTTON:
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ affectedForegroundPermissions, GRANTED_ALWAYS);
+ }
+ break;
+ case ALLOW_FOREGROUND_BUTTON:
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ affectedForegroundPermissions, GRANTED_FOREGROUND_ONLY);
+ }
+ break;
+ case ALLOW_ALWAYS_BUTTON:
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ affectedForegroundPermissions, GRANTED_ALWAYS);
+ }
+ break;
+ case ALLOW_ONE_TIME_BUTTON:
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ affectedForegroundPermissions, GRANTED_ONE_TIME);
+ }
+ break;
+ case DENY_BUTTON:
+ case NO_UPGRADE_BUTTON:
+ case NO_UPGRADE_OT_BUTTON:
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ affectedForegroundPermissions, DENIED);
+ }
+ break;
+ case DENY_AND_DONT_ASK_AGAIN_BUTTON:
+ case NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON:
+ case NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON:
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ affectedForegroundPermissions, DENIED_DO_NOT_ASK_AGAIN);
+ }
+ break;
}
}
@Override
- public void saveInstanceState(Bundle outState) {
- Bundle b = new Bundle();
- b.putByte(SHOW_DO_NOT_ASK, (byte) (mShowDoNotAsk ? 1 : 0));
- b.putString(GROUP_NAME, mGroupName);
- b.putBundle(DIALOG_BUNDLE, mDialog.onSaveInstanceState());
+ public View createView() {
+ WearGrantPermissionsViewModelFactory factory = new WearGrantPermissionsViewModelFactory();
+ mViewModel = factory.create(WearGrantPermissionsViewModel.class);
+ ComposeView root = new ComposeView(mActivity);
+
+ WearGrantPermissionsScreenKt.setContent(root,
+ mViewModel,
+ id -> {
+ onButtonClicked(id);
+ return Unit.INSTANCE;
+ },
+ checked -> {
+ onLocationSwitchChanged(checked);
+ return Unit.INSTANCE;
+ });
+ if (mGroupName != null) {
+ updateScreen();
+ }
- outState.putBundle(WATCH_HANDLER_BUNDLE, b);
+ return root;
}
@Override
- public void loadInstanceState(Bundle savedInstanceState) {
- Bundle b = savedInstanceState.getBundle(WATCH_HANDLER_BUNDLE);
- mShowDoNotAsk = b.getByte(SHOW_DO_NOT_ASK) == 1;
- mGroupName = b.getString(GROUP_NAME);
- showDialog(b.getBundle(DIALOG_BUNDLE));
+ public void updateWindowAttributes(LayoutParams outLayoutParams) {
+ // No-op
}
- @Override
- public void onBackPressed() {
- notifyListener(DENIED);
+ private void setButtonVisibilities(@Nullable boolean[] visibilities) {
+ for (int i = 0; i < mButtonVisibilities.length; i++) {
+ mButtonVisibilities[i] =
+ visibilities != null && i < visibilities.length && visibilities[i];
+ }
}
- @Override
- public void onClick(DialogInterface dialog, int which) {
- switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
- notifyListener(GRANTED_ALWAYS);
- break;
- case DialogInterface.BUTTON_NEUTRAL:
- notifyListener(DENIED);
- break;
- case DialogInterface.BUTTON_NEGATIVE:
- /* In AlertDialog, the negative button is also a don't ask again button. */
- if (dialog instanceof AlertDialog) {
- notifyListener(DENIED_DO_NOT_ASK_AGAIN);
- } else {
- notifyListener(DENIED);
- }
- break;
+ private void setLocationVisibilities(@Nullable boolean[] visibilities) {
+ for (int i = 0; i < mLocationVisibilities.length; i++) {
+ mLocationVisibilities[i] =
+ visibilities != null && i < visibilities.length && visibilities[i];
}
}
- private void notifyListener(@Result int result) {
+ @Override
+ public void onBackPressed() {
if (mResultListener != null) {
- mResultListener.onPermissionGrantResult(mGroupName, result);
+ mResultListener.onPermissionGrantResult(mGroupName, CANCELED);
+ } else {
+ mActivity.finish();
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/ReviewPermissionsWearFragment.java b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/ReviewPermissionsWearFragment.java
index c31aa5d63..5f23829b0 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/ReviewPermissionsWearFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/ReviewPermissionsWearFragment.java
@@ -16,13 +16,21 @@
package com.android.permissioncontroller.permission.ui.wear;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.os.RemoteCallback;
+import android.os.UserHandle;
import android.text.Html;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
@@ -43,8 +51,8 @@ import com.android.permissioncontroller.permission.model.AppPermissionGroup;
import com.android.permissioncontroller.permission.model.AppPermissions;
import com.android.permissioncontroller.permission.utils.Utils;
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
implements Preference.OnPreferenceChangeListener {
@@ -91,11 +99,6 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
mAppPermissions = new AppPermissions(activity, packageInfo, false,
() -> getActivity().finish());
- if (mAppPermissions.getPermissionGroups().isEmpty()) {
- activity.finish();
- return;
- }
-
boolean reviewRequired = false;
for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
if (group.isReviewRequired()) {
@@ -105,6 +108,7 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
}
if (!reviewRequired) {
+ confirmPermissionsReview();
activity.finish();
}
}
@@ -137,19 +141,22 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
final boolean isPackageUpdated = isPackageUpdated();
int permOrder = ORDER_PERM_OFFSET_START;
+ PackageInfo pkg = mAppPermissions.getPackageInfo();
+ ApplicationInfo appInfo = pkg.applicationInfo;
+
for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
if (!Utils.shouldShowPermission(getContext(), group)
|| !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
continue;
}
- final SwitchPreference preference;
+ final PermissionSwitchPreference preference;
Preference cachedPreference = oldNewPermissionsCategory != null
? oldNewPermissionsCategory.findPreference(group.getName()) : null;
- if (cachedPreference instanceof SwitchPreference) {
- preference = (SwitchPreference) cachedPreference;
+ if (cachedPreference instanceof PermissionSwitchPreference) {
+ preference = (PermissionSwitchPreference) cachedPreference;
} else {
- preference = new SwitchPreference(getActivity());
+ preference = new PermissionSwitchPreference(getActivity());
preference.setKey(group.getName());
preference.setTitle(group.getLabel());
@@ -159,7 +166,8 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
preference.setOnPreferenceChangeListener(this);
}
- if (group.isReviewRequired() ) {
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.M &&
+ group.isReviewRequired() ) {
preference.setChecked(true);
} else {
preference.setChecked(group.areRuntimePermissionsGranted());
@@ -171,7 +179,10 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
} else {
preference.setEnabled(true);
}
-
+ if (preference.getParent() != null) {
+ // Remove first if already added.
+ preference.getParent().removePreference(preference);
+ }
if (group.isReviewRequired()) {
if (!isPackageUpdated) {
// An app just being installed, which means all groups requiring reviews.
@@ -213,14 +224,16 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
- Log.d(TAG, "onPreferenceChange " + preference.getTitle());
+ Log.d(TAG, "onPreferenceChange " + preference.getTitle());
if (mHasConfirmedRevoke) {
return true;
}
- if (preference instanceof SwitchPreference) {
- SwitchPreference switchPreference = (SwitchPreference) preference;
- if (switchPreference.isChecked()) {
- showWarnRevokeDialog(switchPreference);
+ if (preference instanceof PermissionSwitchPreference) {
+ PermissionSwitchPreference permPreference = (PermissionSwitchPreference)
+ preference;
+ permPreference.setChanged();
+ if (permPreference.isChecked()) {
+ showWarnRevokeDialog(permPreference);
} else {
return true;
}
@@ -257,26 +270,58 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
}
}
- if (preferenceGroups.isEmpty()) {
- return;
- }
for (PreferenceGroup preferenceGroup: preferenceGroups) {
final int preferenceCount = preferenceGroup.getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
Preference preference = preferenceGroup.getPreference(i);
- if (preference instanceof TwoStatePreference) {
- TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
+ if (preference instanceof PermissionSwitchPreference) {
+ PermissionSwitchPreference permPreference = (PermissionSwitchPreference)
+ preference;
String groupName = preference.getKey();
AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
- if (twoStatePreference.isChecked()) {
- group.grantRuntimePermissions(true, false);
- } else {
- group.revokeRuntimePermissions(false);
+ if (group.isReviewRequired() || permPreference.wasChanged()) {
+ if (permPreference.isChecked()) {
+ Log.i(TAG, groupName + " permPreference.isChecked()");
+ group.grantRuntimePermissions(true, false);
+ } else {
+ Log.i(TAG, groupName + " !permPreference.isChecked()");
+ group.revokeRuntimePermissions(false);
+ }
+ }
+
+ AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
+ if (backgroundGroup != null) {
+ // If the preference wasn't toggled we show it as "fully granted"
+ if (backgroundGroup.isReviewRequired() && !permPreference.wasChanged()) {
+ backgroundGroup.grantRuntimePermissions(true, false);
+ }
+ backgroundGroup.unsetReviewRequired();
}
- group.unsetReviewRequired();
}
}
}
+
+ // Some permission might be restricted and hence there is no AppPermissionGroup for it.
+ // Manually unset all review-required flags, regardless of restriction.
+ PackageManager pm = getContext().getPackageManager();
+ PackageInfo pkg = mAppPermissions.getPackageInfo();
+ UserHandle user = UserHandle.getUserHandleForUid(pkg.applicationInfo.uid);
+
+ if (pkg.requestedPermissions == null) {
+ // No flag updating to do
+ return;
+ }
+
+ for (String perm : pkg.requestedPermissions) {
+ try {
+ pm.updatePermissionFlags(perm, pkg.packageName,
+ FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_USER_SET,
+ FLAG_PERMISSION_USER_SET, user);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Cannot unmark " + perm + " requested by " + pkg.packageName
+ + " as review required", e);
+ }
+ }
}
private void addTitlePreferenceToScreen(PreferenceScreen screen) {
@@ -290,7 +335,7 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
titlePref.setIcon(icon);
// Set message
- String appLabel = mAppPermissions.getAppLabel().toString();
+ String appLabel = Html.escapeHtml(mAppPermissions.getAppLabel().toString());
final int labelTemplateResId = isPackageUpdated()
? R.string.permission_review_title_template_update
: R.string.permission_review_title_template_install;
@@ -379,4 +424,32 @@ public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
callback.sendResult(result);
}
}
+
+ /**
+ * Extend the {@link SwitchPreference}:
+ * <ul>
+ * <li>Monitor the changed state</li>
+ * </ul>
+ */
+ private static class PermissionSwitchPreference extends SwitchPreference {
+ private boolean mWasChanged = false;
+
+ PermissionSwitchPreference(Context context) {
+ super(context);
+ }
+
+ /**
+ * Mark the permission as changed by the user
+ */
+ void setChanged() {
+ mWasChanged = true;
+ }
+
+ /**
+ * @return {@code true} iff the permission was changed by the user
+ */
+ boolean wasChanged() {
+ return mWasChanged;
+ }
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt
new file mode 100644
index 000000000..3936e54d4
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionFragment.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.Manifest
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.os.UserHandle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.StringRes
+import androidx.compose.ui.platform.ComposeView
+import androidx.core.os.BundleCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.Constants
+import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION
+import com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS
+import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY
+import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
+import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ConfirmDialogShowingFragment
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory
+import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs
+import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionConfirmDialogViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionConfirmDialogViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.model.ConfirmDialogArgs
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel
+import com.android.settingslib.RestrictedLockUtils
+
+/**
+ * Show and manage a single permission group for an app.
+ *
+ * <p>Allows the user to control whether the app is granted the permission
+ *
+ * <p>
+ * Based on AppPermissionFragment in handheld code.
+ */
+class WearAppPermissionFragment : Fragment(), ConfirmDialogShowingFragment {
+
+ private lateinit var confirmDialogViewModel: AppPermissionConfirmDialogViewModel
+
+ companion object {
+ private const val GRANT_CATEGORY = "grant_category"
+
+ /**
+ * Create a bundle with the arguments needed by this fragment
+ *
+ * @param packageName The name of the package
+ * @param permName The name of the permission whose group this fragment is for (optional)
+ * @param groupName The name of the permission group (required if permName not specified)
+ * @param userHandle The user of the app permission group
+ * @param caller The name of the fragment we called from
+ * @param sessionId The current session ID
+ * @param grantCategory The grant status of this app permission group. Used to initially set
+ * the button state
+ * @return A bundle with all of the args placed
+ */
+ @JvmStatic
+ fun createArgs(
+ packageName: String?,
+ permName: String?,
+ groupName: String?,
+ userHandle: UserHandle?,
+ caller: String?,
+ sessionId: Long,
+ grantCategory: String?
+ ): Bundle {
+ val arguments = Bundle()
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName)
+ if (groupName == null) {
+ arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName)
+ } else {
+ arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
+ }
+ arguments.putParcelable(Intent.EXTRA_USER, userHandle)
+ arguments.putString(EXTRA_CALLER_NAME, caller)
+ arguments.putLong(EXTRA_SESSION_ID, sessionId)
+ arguments.putString(GRANT_CATEGORY, grantCategory)
+ return arguments
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val activity = requireActivity()
+ val packageName =
+ arguments?.getString(Intent.EXTRA_PACKAGE_NAME)
+ ?: throw RuntimeException("Package name must not be null.")
+ val permGroupName =
+ arguments?.getString(Intent.EXTRA_PERMISSION_GROUP_NAME)
+ ?: arguments?.getString(Intent.EXTRA_PERMISSION_NAME)
+ ?: throw RuntimeException("Permission name must not be null.")
+
+ val isStorageGroup = permGroupName == Manifest.permission_group.STORAGE
+
+ val user =
+ arguments?.let {
+ BundleCompat.getParcelable(it, Intent.EXTRA_USER, UserHandle::class.java)
+ }
+ ?: UserHandle.SYSTEM
+ val permGroupLabel = getPermGroupLabel(activity, permGroupName).toString()
+
+ val sessionId = arguments?.getLong(EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID
+
+ val factory =
+ AppPermissionViewModelFactory(
+ activity.getApplication(),
+ packageName,
+ permGroupName,
+ user,
+ sessionId
+ )
+ val viewModel = ViewModelProvider(this, factory).get(AppPermissionViewModel::class.java)
+ confirmDialogViewModel =
+ ViewModelProvider(this, AppPermissionConfirmDialogViewModelFactory())
+ .get(AppPermissionConfirmDialogViewModel::class.java)
+
+ @Suppress("ktlint:standard:max-line-length")
+ val onLocationSwitchChanged: (Boolean) -> Unit = { checked ->
+ run {
+ val changeRequest =
+ if (checked) {
+ ChangeRequest.GRANT_FINE_LOCATION
+ } else {
+ ChangeRequest.REVOKE_FINE_LOCATION
+ }
+ val buttonClicked =
+ if (checked) {
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__GRANT_FINE_LOCATION
+ } else {
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__REVOKE_FINE_LOCATION
+ }
+ viewModel.requestChange(false, this, this, changeRequest, buttonClicked)
+ }
+ }
+ val onGrantedStateChanged: (ButtonType, Boolean) -> Unit = { buttonType, checked ->
+ run {
+ if (!checked) {
+ return@run
+ }
+ val param = getGrantedStateChangeParam(buttonType)
+ if (!isStorageGroup || !param.requiresCustomStorageBehavior) {
+ viewModel.requestChange(
+ param.setOneTime,
+ this,
+ this,
+ param.request,
+ param.buttonClickAction
+ )
+ } else {
+ showConfirmDialog(
+ ChangeRequest.GRANT_ALL_FILE_ACCESS,
+ R.string.special_file_access_dialog,
+ -1,
+ false
+ )
+ }
+ setResult(param.result, permGroupName)
+ }
+ }
+ val onFooterClicked: (RestrictedLockUtils.EnforcedAdmin) -> Unit = { admin ->
+ run { RestrictedLockUtils.sendShowAdminSupportDetailsIntent(requireContext(), admin) }
+ }
+ val onConfirmDialogOkButtonClick: (ConfirmDialogArgs) -> Unit = { args ->
+ run {
+ if (args.changeRequest == ChangeRequest.GRANT_ALL_FILE_ACCESS) {
+ viewModel.setAllFilesAccess(true)
+ viewModel.requestChange(
+ false,
+ this,
+ this,
+ ChangeRequest.GRANT_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW
+ )
+ } else {
+ viewModel.onDenyAnyWay(args.changeRequest, args.buttonPressed, args.oneTime)
+ }
+ confirmDialogViewModel.showConfirmDialogLiveData.value = false
+ }
+ }
+ val onConfirmDialogCancelButtonClick: () -> Unit = {
+ confirmDialogViewModel.showConfirmDialogLiveData.value = false
+ }
+ val onAdvancedConfirmDialogOkButtonClick: (AdvancedConfirmDialogArgs) -> Unit = { args ->
+ run {
+ viewModel.requestChange(
+ args.setOneTime!!,
+ this,
+ this,
+ args.changeRequest!!,
+ args.buttonClicked!!
+ )
+ confirmDialogViewModel.showAdvancedConfirmDialogLiveData.value = false
+ }
+ }
+ val onAdvancedConfirmDialogCancelButtonClick: () -> Unit = {
+ confirmDialogViewModel.showAdvancedConfirmDialogLiveData.value = false
+ }
+
+ return ComposeView(activity).apply {
+ setContent {
+ WearPermissionTheme {
+ WearAppPermissionScreen(
+ permGroupLabel,
+ viewModel,
+ confirmDialogViewModel,
+ onLocationSwitchChanged,
+ onGrantedStateChanged,
+ onFooterClicked,
+ onConfirmDialogOkButtonClick,
+ onConfirmDialogCancelButtonClick,
+ onAdvancedConfirmDialogOkButtonClick,
+ onAdvancedConfirmDialogCancelButtonClick
+ )
+ }
+ }
+ }
+ }
+
+ override fun showConfirmDialog(
+ changeRequest: ChangeRequest,
+ @StringRes messageId: Int,
+ buttonPressed: Int,
+ oneTime: Boolean
+ ) {
+ confirmDialogViewModel.confirmDialogArgs =
+ ConfirmDialogArgs(
+ messageId = messageId,
+ changeRequest = changeRequest,
+ buttonPressed = buttonPressed,
+ oneTime = oneTime
+ )
+ confirmDialogViewModel.showConfirmDialogLiveData.value = true
+ }
+
+ override fun showAdvancedConfirmDialog(args: AdvancedConfirmDialogArgs) {
+ confirmDialogViewModel.advancedConfirmDialogArgs = args
+ confirmDialogViewModel.showAdvancedConfirmDialogLiveData.value = true
+ }
+
+ private fun setResult(@GrantPermissionsViewHandler.Result result: Int, permGroupName: String) {
+ val intent: Intent =
+ Intent()
+ .putExtra(
+ ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED,
+ permGroupName
+ )
+ .putExtra(ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT, result)
+ requireActivity().setResult(Activity.RESULT_OK, intent)
+ }
+
+ fun getGrantedStateChangeParam(buttonType: ButtonType) =
+ when (buttonType) {
+ ButtonType.ALLOW ->
+ GrantedStateChangeParam(
+ false,
+ ChangeRequest.GRANT_FOREGROUND,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW,
+ GRANTED_ALWAYS,
+ false
+ )
+ ButtonType.ALLOW_ALWAYS ->
+ GrantedStateChangeParam(
+ false,
+ ChangeRequest.GRANT_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS,
+ GRANTED_ALWAYS,
+ true
+ )
+ ButtonType.ALLOW_FOREGROUND ->
+ GrantedStateChangeParam(
+ false,
+ ChangeRequest.GRANT_FOREGROUND_ONLY,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND,
+ GRANTED_FOREGROUND_ONLY,
+ true
+ )
+ ButtonType.ASK ->
+ GrantedStateChangeParam(
+ true,
+ ChangeRequest.REVOKE_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME,
+ DENIED,
+ false
+ )
+ ButtonType.DENY ->
+ GrantedStateChangeParam(
+ false,
+ ChangeRequest.REVOKE_BOTH,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY,
+ DENIED_DO_NOT_ASK_AGAIN,
+ false
+ )
+ ButtonType.DENY_FOREGROUND ->
+ GrantedStateChangeParam(
+ false,
+ ChangeRequest.REVOKE_FOREGROUND,
+ APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND,
+ DENIED_DO_NOT_ASK_AGAIN,
+ false
+ )
+ else -> throw RuntimeException("Wrong button type: $buttonType")
+ }
+}
+
+data class GrantedStateChangeParam(
+ val setOneTime: Boolean,
+ val request: ChangeRequest,
+ val buttonClickAction: Int,
+ val result: Int,
+ val requiresCustomStorageBehavior: Boolean
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt
new file mode 100644
index 000000000..9b960dfb5
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsFragment.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.os.UserHandle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.platform.ComposeView
+import androidx.core.os.BundleCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.model.AppPermissions
+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.AppPermissionGroupsViewModel
+import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled
+import java.time.Instant
+import java.util.concurrent.TimeUnit
+
+class WearAppPermissionGroupsFragment : Fragment(), PermissionsUsagesChangeCallback {
+ private lateinit var permissionUsages: PermissionUsages
+ private lateinit var wearViewModel: WearAppPermissionUsagesViewModel
+ private lateinit var helper: WearAppPermissionGroupsHelper
+
+ // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors
+ // are using the class to load PermissionUsages.
+ @Suppress("DEPRECATION")
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val packageName = arguments?.getString(Intent.EXTRA_PACKAGE_NAME) ?: ""
+ val user =
+ arguments?.let {
+ BundleCompat.getParcelable(it, Intent.EXTRA_USER, UserHandle::class.java)!!
+ }
+ ?: UserHandle.SYSTEM
+
+ val activity: Activity = requireActivity()
+ val packageManager = activity.packageManager
+
+ val packageInfo: PackageInfo? =
+ try {
+ packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e)
+ null
+ }
+
+ if (packageInfo == null) {
+ Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show()
+ activity.finish()
+ return null
+ }
+ val sessionId = arguments?.getLong(EXTRA_SESSION_ID, 0) ?: 0
+ val appPermissions = AppPermissions(activity, packageInfo, true, { activity.finish() })
+ val factory = AppPermissionGroupsViewModelFactory(packageName, user, sessionId)
+ val viewModel =
+ ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel::class.java)
+ wearViewModel =
+ ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory())
+ .get(WearAppPermissionUsagesViewModel::class.java)
+ val revokeDialogViewModel =
+ ViewModelProvider(this, AppPermissionGroupsRevokeDialogViewModelFactory())
+ .get(AppPermissionGroupsRevokeDialogViewModel::class.java)
+
+ val context = requireContext()
+
+ // If the build type is below S, the app ops for permission usage can't be found. Thus, we
+ // shouldn't load permission usages, for them.
+ if (SdkLevel.isAtLeastS()) {
+ permissionUsages = PermissionUsages(context)
+ val aggregateDataFilterBeginDays =
+ (if (is7DayToggleEnabled())
+ AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_7
+ else AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS_1)
+ .toLong()
+
+ val filterTimeBeginMillis =
+ Math.max(
+ System.currentTimeMillis() -
+ TimeUnit.DAYS.toMillis(aggregateDataFilterBeginDays),
+ Instant.EPOCH.toEpochMilli()
+ )
+ permissionUsages.load(
+ null,
+ null,
+ filterTimeBeginMillis,
+ Long.MAX_VALUE,
+ PermissionUsages.USAGE_FLAG_LAST,
+ requireActivity().getLoaderManager(),
+ false,
+ false,
+ this,
+ false
+ )
+ }
+ helper =
+ WearAppPermissionGroupsHelper(
+ context = context,
+ fragment = this,
+ user = user,
+ packageName = packageName,
+ sessionId = sessionId,
+ appPermissions = appPermissions,
+ viewModel = viewModel,
+ wearViewModel = wearViewModel,
+ revokeDialogViewModel = revokeDialogViewModel
+ )
+
+ return ComposeView(activity).apply {
+ setContent { WearPermissionTheme { WearAppPermissionGroupsScreen(helper) } }
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ helper.logAndClearToggledGroups()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.S)
+ override fun onPermissionUsagesChanged() {
+ if (permissionUsages.usages.isEmpty()) {
+ return
+ }
+ if (getContext() == null) {
+ // Async result has come in after our context is gone.
+ return
+ }
+ wearViewModel.appPermissionUsages.value =
+ ArrayList<AppPermissionUsage>(permissionUsages.usages)
+ }
+
+ companion object {
+ const val LOG_TAG = "WearAppPermissionGroups"
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt
new file mode 100644
index 000000000..9529ac83a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsHelper.kt
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PermissionInfo
+import android.os.Build
+import android.os.UserHandle
+import android.util.ArraySet
+import android.util.Log
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.findNavController
+import com.android.permission.flags.Flags
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.hibernation.isHibernationEnabled
+import com.android.permissioncontroller.permission.model.AppPermissionGroup
+import com.android.permissioncontroller.permission.model.AppPermissions
+import com.android.permissioncontroller.permission.model.Permission
+import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState
+import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
+import com.android.permissioncontroller.permission.ui.Category
+import com.android.permissioncontroller.permission.ui.LocationProviderInterceptDialog
+import com.android.permissioncontroller.permission.ui.handheld.AppPermissionFragment
+import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel
+import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.GroupUiInfo
+import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.PermSubtitle
+import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionGroupsRevokeDialogViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs
+import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel
+import com.android.permissioncontroller.permission.utils.ArrayUtils
+import com.android.permissioncontroller.permission.utils.LocationUtils
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.permission.utils.legacy.LegacySafetyNetLogger
+import com.android.permissioncontroller.permission.utils.navigateSafe
+
+class WearAppPermissionGroupsHelper(
+ val context: Context,
+ val fragment: Fragment,
+ val user: UserHandle,
+ val packageName: String,
+ val sessionId: Long,
+ private val appPermissions: AppPermissions,
+ val viewModel: AppPermissionGroupsViewModel,
+ val wearViewModel: WearAppPermissionUsagesViewModel,
+ val revokeDialogViewModel: AppPermissionGroupsRevokeDialogViewModel,
+ private val toggledGroups: ArraySet<AppPermissionGroup> = ArraySet()
+) {
+ fun getPermissionGroupChipParams(
+ appPermissionUsages: List<AppPermissionUsage>
+ ): List<PermissionGroupChipParam> {
+ if (DEBUG) {
+ Log.d(TAG, "getPermissionGroupChipParams() called")
+ }
+ val groupUsageLastAccessTime: MutableMap<String, Long> = HashMap()
+ viewModel.extractGroupUsageLastAccessTime(
+ groupUsageLastAccessTime,
+ appPermissionUsages,
+ packageName
+ )
+ val groupUiInfos = viewModel.packagePermGroupsLiveData.value
+ val groups: List<AppPermissionGroup> = appPermissions.permissionGroups
+
+ val grantedTypes: MutableMap<String, Category> = HashMap()
+ val bookKeeping: MutableMap<String, GroupUiInfo> = HashMap()
+ if (groupUiInfos != null) {
+ for (category in groupUiInfos.keys) {
+ val groupInfoList: List<GroupUiInfo> = groupUiInfos[category] ?: emptyList()
+ for (groupInfo in groupInfoList) {
+ bookKeeping[groupInfo.groupName] = groupInfo
+ grantedTypes[groupInfo.groupName] = category
+ }
+ }
+ }
+
+ val list: MutableList<PermissionGroupChipParam> = ArrayList()
+
+ groups
+ .filter { Utils.shouldShowPermission(context, it) }
+ .partition { it.declaringPackage == Utils.OS_PKG }
+ .let { it.first.plus(it.second) }
+ .forEach { group ->
+ if (Utils.areGroupPermissionsIndividuallyControlled(context, group.name)) {
+ // If permission is controlled individually, we show all requested permission
+ // inside this group.
+ for (perm in getPermissionInfosFromGroup(group)) {
+ list.add(
+ PermissionGroupChipParam(
+ group = group,
+ perm = perm,
+ label = perm.loadLabel(context.packageManager).toString(),
+ checked = group.areRuntimePermissionsGranted(arrayOf(perm.name)),
+ onCheckedChanged = { checked ->
+ run { onPermissionGrantedStateChanged(group, perm, checked) }
+ }
+ )
+ )
+ }
+ } else {
+ val category = grantedTypes[group.name]
+ if (category != null) {
+ list.add(
+ PermissionGroupChipParam(
+ group = group,
+ label = group.label.toString(),
+ summary =
+ bookKeeping[group.name]?.let {
+ getSummary(
+ category,
+ it,
+ groupUsageLastAccessTime[it.groupName]
+ )
+ },
+ onClick = { onPermissionGroupClicked(group, category.categoryName) }
+ )
+ )
+ }
+ }
+ }
+ return list
+ }
+
+ private fun getSummary(
+ category: Category?,
+ groupUiInfo: GroupUiInfo,
+ lastAccessTime: Long?
+ ): String {
+ val grantSummary =
+ getGrantSummary(category, groupUiInfo)?.let { context.getString(it) } ?: ""
+ val summary = StringBuilder(grantSummary)
+ if (Flags.wearPrivacyDashboardEnabledReadOnly()) {
+ WearUtils.getPreferenceSummary(context, lastAccessTime).let {
+ if (it.isNotEmpty()) {
+ summary.append(System.lineSeparator()).append(it)
+ }
+ }
+ }
+ return summary.toString()
+ }
+
+ private fun getGrantSummary(category: Category?, groupUiInfo: GroupUiInfo): Int? {
+ val subtitle = groupUiInfo.subtitle
+ if (category != null) {
+ when (category) {
+ Category.ALLOWED ->
+ return if (subtitle == PermSubtitle.BACKGROUND) {
+ R.string.allowed_always_header
+ } else {
+ R.string.allowed_header
+ }
+ Category.ASK -> return R.string.ask_header
+ Category.DENIED -> return R.string.denied_header
+ else -> {
+ /* Fallback though */
+ }
+ }
+ }
+ return when (subtitle) {
+ PermSubtitle.FOREGROUND_ONLY -> R.string.permission_subtitle_only_in_foreground
+ PermSubtitle.MEDIA_ONLY -> R.string.permission_subtitle_media_only
+ PermSubtitle.ALL_FILES -> R.string.permission_subtitle_all_files
+ else -> null
+ }
+ }
+
+ private fun getPermissionInfosFromGroup(group: AppPermissionGroup): List<PermissionInfo> =
+ group.permissions
+ .map {
+ it?.let {
+ try {
+ context.packageManager.getPermissionInfo(it.name, 0)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "No permission:" + it.name)
+ null
+ }
+ }
+ }
+ .filterNotNull()
+ .toList()
+
+ private fun onPermissionGrantedStateChanged(
+ group: AppPermissionGroup,
+ perm: PermissionInfo,
+ checked: Boolean
+ ) {
+ if (checked) {
+ group.grantRuntimePermissions(true, false, arrayOf(perm.name))
+
+ if (
+ Utils.areGroupPermissionsIndividuallyControlled(context, group.name) &&
+ group.doesSupportRuntimePermissions()
+ ) {
+ // We are granting a permission from a group but since this is an
+ // individual permission control other permissions in the group may
+ // be revoked, hence we need to mark them user fixed to prevent the
+ // app from requesting a non-granted permission and it being granted
+ // because another permission in the group is granted. This applies
+ // only to apps that support runtime permissions.
+ var revokedPermissionsToFix: Array<String?>? = null
+ val permissionCount = group.permissions.size
+ for (i in 0 until permissionCount) {
+ val current = group.permissions[i]
+ if (!current.isGranted && !current.isUserFixed) {
+ revokedPermissionsToFix =
+ ArrayUtils.appendString(revokedPermissionsToFix, current.name)
+ }
+ }
+ if (revokedPermissionsToFix != null) {
+ // If some permissions were not granted then they should be fixed.
+ group.revokeRuntimePermissions(true, revokedPermissionsToFix)
+ }
+ }
+ } else {
+ val appPerm: Permission = getPermissionFromGroup(group, perm.name) ?: return
+
+ val grantedByDefault = appPerm.isGrantedByDefault
+ if (
+ grantedByDefault ||
+ (!group.doesSupportRuntimePermissions() &&
+ !revokeDialogViewModel.hasConfirmedRevoke)
+ ) {
+ showRevocationWarningDialog(
+ messageId =
+ if (grantedByDefault) {
+ R.string.system_warning
+ } else {
+ R.string.old_sdk_deny_warning
+ },
+ onOkButtonClick = {
+ revokePermissionInGroup(group, perm.name)
+ if (!appPerm.isGrantedByDefault) {
+ revokeDialogViewModel.hasConfirmedRevoke = true
+ }
+ revokeDialogViewModel.dismissDialog()
+ }
+ )
+ } else {
+ revokePermissionInGroup(group, perm.name)
+ }
+ }
+ }
+
+ private fun getPermissionFromGroup(group: AppPermissionGroup, permName: String): Permission? {
+ return group.permissions.find { it.name == permName }
+ ?: let {
+ if ("user" == Build.TYPE) {
+ Log.e(
+ TAG,
+ "The impossible happens, permission $permName is not in group $group.name."
+ )
+ null
+ } else {
+ // This is impossible, throw a fatal error in non-user build.
+ throw IllegalArgumentException(
+ "Permission $permName is not in group $group.name%s"
+ )
+ }
+ }
+ }
+
+ private fun revokePermissionInGroup(group: AppPermissionGroup, permName: String) {
+ group.revokeRuntimePermissions(true, arrayOf(permName))
+
+ if (
+ Utils.areGroupPermissionsIndividuallyControlled(context, group.name) &&
+ group.doesSupportRuntimePermissions() &&
+ !group.areRuntimePermissionsGranted()
+ ) {
+ // If we just revoked the last permission we need to clear
+ // the user fixed state as now the app should be able to
+ // request them at runtime if supported.
+ group.revokeRuntimePermissions(false)
+ }
+ }
+
+ private fun showRevocationWarningDialog(
+ messageId: Int,
+ onOkButtonClick: () -> Unit,
+ onCancelButtonClick: () -> Unit = { revokeDialogViewModel.dismissDialog() }
+ ) {
+ revokeDialogViewModel.revokeDialogArgs =
+ RevokeDialogArgs(
+ messageId = messageId,
+ onOkButtonClick = onOkButtonClick,
+ onCancelButtonClick = onCancelButtonClick
+ )
+ revokeDialogViewModel.showDialogLiveData.value = true
+ }
+
+ private fun onPermissionGroupClicked(group: AppPermissionGroup, grantCategory: String) {
+ val permGroupName = group.name
+ val packageName = group.app?.packageName ?: ""
+ val caller = WearAppPermissionGroupsFragment::class.java.name
+
+ addToggledGroup(group)
+
+ if (LocationUtils.isLocationGroupAndProvider(context, permGroupName, packageName)) {
+ val intent = Intent(context, LocationProviderInterceptDialog::class.java)
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+ context.startActivityAsUser(intent, user)
+ } else if (
+ LocationUtils.isLocationGroupAndControllerExtraPackage(
+ context,
+ permGroupName,
+ packageName
+ )
+ ) {
+ // Redirect to location controller extra package settings.
+ LocationUtils.startLocationControllerExtraPackageSettings(context, user)
+ } else {
+ val args =
+ AppPermissionFragment.createArgs(
+ packageName,
+ null,
+ permGroupName,
+ user,
+ caller,
+ sessionId,
+ grantCategory
+ )
+ fragment.findNavController().navigateSafe(R.id.perm_groups_to_app, args)
+ }
+ }
+
+ private fun addToggledGroup(group: AppPermissionGroup) {
+ toggledGroups.add(group)
+ }
+
+ fun logAndClearToggledGroups() {
+ LegacySafetyNetLogger.logPermissionsToggled(toggledGroups)
+ toggledGroups.clear()
+ }
+
+ fun getAutoRevokeChipParam(state: HibernationSettingState?): AutoRevokeChipParam? =
+ state?.let {
+ AutoRevokeChipParam(
+ labelRes =
+ if (isHibernationEnabled()) {
+ R.string.unused_apps_label_v2
+ } else {
+ R.string.auto_revoke_label
+ },
+ visible = it.revocableGroupNames.isNotEmpty(),
+ checked = it.isEligibleForHibernation(),
+ onCheckedChanged = { checked ->
+ run {
+ viewModel.setAutoRevoke(checked)
+ Log.w(TAG, "setAutoRevoke $checked")
+ }
+ }
+ )
+ }
+
+ companion object {
+ const val DEBUG = false
+ const val TAG = WearAppPermissionGroupsFragment.LOG_TAG
+ }
+}
+
+data class PermissionGroupChipParam(
+ val group: AppPermissionGroup,
+ val perm: PermissionInfo? = null,
+ val label: String,
+ val summary: String? = null,
+ val enabled: Boolean = true,
+ val checked: Boolean? = null,
+ val onClick: () -> Unit = {},
+ val onCheckedChanged: (Boolean) -> Unit = {}
+)
+
+data class AutoRevokeChipParam(
+ val labelRes: Int,
+ val visible: Boolean,
+ val checked: Boolean = false,
+ val onCheckedChanged: (Boolean) -> Unit
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt
new file mode 100644
index 000000000..0883666fc
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.res.stringResource
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.wear.elements.AlertDialog
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
+import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs
+
+@Composable
+fun WearAppPermissionGroupsScreen(helper: WearAppPermissionGroupsHelper) {
+ val packagePermGroups = helper.viewModel.packagePermGroupsLiveData.observeAsState(null)
+ val autoRevoke = helper.viewModel.autoRevokeLiveData.observeAsState(null)
+ val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList())
+ val showRevokeDialog = helper.revokeDialogViewModel.showDialogLiveData.observeAsState(false)
+ var isLoading by remember { mutableStateOf(true) }
+
+ Box {
+ WearAppPermissionGroupsContent(
+ isLoading,
+ helper.getPermissionGroupChipParams(appPermissionUsages.value),
+ helper.getAutoRevokeChipParam(autoRevoke.value)
+ )
+ RevokeDialog(
+ showDialog = showRevokeDialog.value,
+ args = helper.revokeDialogViewModel.revokeDialogArgs
+ )
+ }
+
+ if (isLoading && !packagePermGroups.value.isNullOrEmpty()) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun WearAppPermissionGroupsContent(
+ isLoading: Boolean,
+ permissionGroupChipParams: List<PermissionGroupChipParam>,
+ autoRevokeChipParam: AutoRevokeChipParam?
+) {
+ ScrollableScreen(title = stringResource(R.string.app_permissions), isLoading = isLoading) {
+ if (permissionGroupChipParams.isEmpty()) {
+ item { Chip(label = stringResource(R.string.no_permissions), onClick = {}) }
+ } else {
+ for (info in permissionGroupChipParams) {
+ item {
+ if (info.checked != null) {
+ ToggleChip(
+ checked = info.checked,
+ label = info.label,
+ enabled = info.enabled,
+ toggleControl = ToggleChipToggleControl.Switch,
+ onCheckedChanged = info.onCheckedChanged
+ )
+ } else {
+ Chip(
+ label = info.label,
+ labelMaxLines = Integer.MAX_VALUE,
+ secondaryLabel = info.summary?.let { info.summary },
+ secondaryLabelMaxLines = Integer.MAX_VALUE,
+ enabled = info.enabled,
+ onClick = info.onClick
+ )
+ }
+ }
+ }
+ autoRevokeChipParam?.let {
+ if (it.visible) {
+ item {
+ ToggleChip(
+ checked = it.checked,
+ label = stringResource(it.labelRes),
+ labelMaxLine = 3,
+ toggleControl = ToggleChipToggleControl.Switch,
+ onCheckedChanged = it.onCheckedChanged
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+internal fun RevokeDialog(showDialog: Boolean, args: RevokeDialogArgs?) {
+ args?.let {
+ AlertDialog(
+ showDialog = showDialog,
+ message = stringResource(it.messageId),
+ onOKButtonClick = it.onOkButtonClick,
+ onCancelButtonClick = it.onCancelButtonClick,
+ scalingLazyListState = rememberScalingLazyListState()
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt
new file mode 100644
index 000000000..ccbd51f7d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.res.stringResource
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType
+import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs
+import com.android.permissioncontroller.permission.ui.wear.elements.AlertDialog
+import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
+import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionConfirmDialogViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.ConfirmDialogArgs
+import com.android.settingslib.RestrictedLockUtils
+
+@Composable
+fun WearAppPermissionScreen(
+ title: String,
+ viewModel: AppPermissionViewModel,
+ confirmDialogViewModel: AppPermissionConfirmDialogViewModel,
+ onLocationSwitchChanged: (Boolean) -> Unit,
+ onGrantedStateChanged: (ButtonType, Boolean) -> Unit,
+ onFooterClicked: (RestrictedLockUtils.EnforcedAdmin) -> Unit,
+ onConfirmDialogOkButtonClick: (ConfirmDialogArgs) -> Unit,
+ onConfirmDialogCancelButtonClick: () -> Unit,
+ onAdvancedConfirmDialogOkButtonClick: (AdvancedConfirmDialogArgs) -> Unit,
+ onAdvancedConfirmDialogCancelButtonClick: () -> Unit
+) {
+ val buttonState = viewModel.buttonStateLiveData.observeAsState(null)
+ val detailResIds = viewModel.detailResIdLiveData.observeAsState(null)
+ val admin = viewModel.showAdminSupportLiveData.observeAsState(null)
+ var isLoading by remember { mutableStateOf(true) }
+ val showConfirmDialog = confirmDialogViewModel.showConfirmDialogLiveData.observeAsState(false)
+ val showAdvancedConfirmDialog =
+ confirmDialogViewModel.showAdvancedConfirmDialogLiveData.observeAsState(false)
+
+ Box {
+ WearAppPermissionContent(
+ title,
+ buttonState.value,
+ detailResIds.value,
+ admin.value,
+ isLoading,
+ onLocationSwitchChanged,
+ onGrantedStateChanged,
+ onFooterClicked,
+ )
+ ConfirmDialog(
+ showDialog = showConfirmDialog.value,
+ args = confirmDialogViewModel.confirmDialogArgs,
+ onOkButtonClick = onConfirmDialogOkButtonClick,
+ onCancelButtonClick = onConfirmDialogCancelButtonClick
+ )
+ AdvancedConfirmDialog(
+ showDialog = showAdvancedConfirmDialog.value,
+ args = confirmDialogViewModel.advancedConfirmDialogArgs,
+ onOkButtonClick = onAdvancedConfirmDialogOkButtonClick,
+ onCancelButtonClick = onAdvancedConfirmDialogCancelButtonClick
+ )
+ }
+ if (isLoading && !buttonState.value.isNullOrEmpty()) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun WearAppPermissionContent(
+ title: String,
+ buttonState: Map<ButtonType, ButtonState>?,
+ detailResIds: Pair<Int, Int?>?,
+ admin: RestrictedLockUtils.EnforcedAdmin?,
+ isLoading: Boolean,
+ onLocationSwitchChanged: (Boolean) -> Unit,
+ onGrantedStateChanged: (ButtonType, Boolean) -> Unit,
+ onFooterClicked: (RestrictedLockUtils.EnforcedAdmin) -> Unit
+) {
+ ScrollableScreen(title = title, isLoading = isLoading) {
+ buttonState?.get(ButtonType.LOCATION_ACCURACY)?.let {
+ if (it.isShown) {
+ item {
+ ToggleChip(
+ checked = it.isChecked,
+ enabled = it.isEnabled,
+ label = stringResource(R.string.app_permission_location_accuracy),
+ toggleControl = ToggleChipToggleControl.Switch,
+ onCheckedChanged = onLocationSwitchChanged,
+ labelMaxLine = Integer.MAX_VALUE
+ )
+ }
+ }
+ }
+ for (buttonType in buttonTypeOrder) {
+ buttonState?.get(buttonType)?.let {
+ if (it.isShown) {
+ item {
+ ToggleChip(
+ checked = it.isChecked,
+ enabled = it.isEnabled,
+ label = labelsByButton(buttonType),
+ toggleControl = ToggleChipToggleControl.Radio,
+ onCheckedChanged = { checked ->
+ onGrantedStateChanged(buttonType, checked)
+ },
+ labelMaxLine = Integer.MAX_VALUE
+ )
+ }
+ }
+ }
+ }
+ detailResIds?.let {
+ item {
+ ListFooter(
+ description = stringResource(detailResIds.first),
+ iconRes = R.drawable.ic_info,
+ onClick =
+ if (admin != null) {
+ { onFooterClicked(admin) }
+ } else {
+ null
+ }
+ )
+ }
+ }
+ }
+}
+
+internal val buttonTypeOrder =
+ listOf(
+ ButtonType.ALLOW,
+ ButtonType.ALLOW_ALWAYS,
+ ButtonType.ALLOW_FOREGROUND,
+ ButtonType.ASK_ONCE,
+ ButtonType.ASK,
+ ButtonType.DENY,
+ ButtonType.DENY_FOREGROUND
+ )
+
+@Composable
+internal fun labelsByButton(buttonType: ButtonType) =
+ when (buttonType) {
+ ButtonType.ALLOW -> stringResource(R.string.app_permission_button_allow)
+ ButtonType.ALLOW_ALWAYS -> stringResource(R.string.app_permission_button_allow_always)
+ ButtonType.ALLOW_FOREGROUND ->
+ stringResource(R.string.app_permission_button_allow_foreground)
+ ButtonType.ASK_ONCE -> stringResource(R.string.app_permission_button_ask)
+ ButtonType.ASK -> stringResource(R.string.app_permission_button_ask)
+ ButtonType.DENY -> stringResource(R.string.app_permission_button_deny)
+ ButtonType.DENY_FOREGROUND -> stringResource(R.string.app_permission_button_deny)
+ else -> ""
+ }
+
+@Composable
+internal fun ConfirmDialog(
+ showDialog: Boolean,
+ args: ConfirmDialogArgs?,
+ onOkButtonClick: (ConfirmDialogArgs) -> Unit,
+ onCancelButtonClick: () -> Unit
+) {
+ args?.let {
+ AlertDialog(
+ showDialog = showDialog,
+ message = stringResource(it.messageId),
+ onOKButtonClick = { onOkButtonClick(it) },
+ onCancelButtonClick = onCancelButtonClick,
+ scalingLazyListState = rememberScalingLazyListState()
+ )
+ }
+}
+
+@Composable
+internal fun AdvancedConfirmDialog(
+ showDialog: Boolean,
+ args: AdvancedConfirmDialogArgs?,
+ onOkButtonClick: (AdvancedConfirmDialogArgs) -> Unit,
+ onCancelButtonClick: () -> Unit
+) {
+ args?.let {
+ AlertDialog(
+ showDialog = showDialog,
+ title =
+ if (it.titleId != 0) {
+ stringResource(it.titleId)
+ } else {
+ ""
+ },
+ iconRes = it.iconId,
+ message = stringResource(it.messageId),
+ okButtonContentDescription = stringResource(it.positiveButtonTextId),
+ cancelButtonContentDescription = stringResource(it.negativeButtonTextId),
+ onOKButtonClick = { onOkButtonClick(it) },
+ onCancelButtonClick = onCancelButtonClick,
+ scalingLazyListState = rememberScalingLazyListState()
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
new file mode 100644
index 000000000..950353f52
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.res.stringResource
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
+import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON
+import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler.BUTTON_RES_ID_TO_NUM
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
+import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModel
+
+@Composable
+fun WearGrantPermissionsScreen(
+ viewModel: WearGrantPermissionsViewModel,
+ onButtonClicked: (Int) -> Unit,
+ onLocationSwitchChanged: (Boolean) -> Unit
+) {
+ val groupMessage = viewModel.groupMessageLiveData.observeAsState("")
+ val icon = viewModel.iconLiveData.observeAsState(null)
+ val detailMessage = viewModel.detailMessageLiveData.observeAsState(null)
+ val locationVisibilities = viewModel.locationVisibilitiesLiveData.observeAsState(emptyList())
+ val preciseLocationChecked = viewModel.preciseLocationCheckedLiveData.observeAsState(false)
+ val buttonVisibilities = viewModel.buttonVisibilitiesLiveData.observeAsState(emptyList())
+
+ ScrollableScreen(
+ showTimeText = false,
+ image = icon.value,
+ title = groupMessage.value,
+ subtitle = detailMessage.value,
+ titleTestTag = "com.android.permissioncontroller:id/permission_message",
+ subtitleTestTag = "com.android.permissioncontroller:id/detail_message",
+ ) {
+ if (
+ locationVisibilities.value.getOrElse(LOCATION_ACCURACY_LAYOUT) { false } &&
+ locationVisibilities.value.getOrElse(DIALOG_WITH_BOTH_LOCATIONS) { false }
+ ) {
+ item {
+ ToggleChip(
+ checked = preciseLocationChecked.value,
+ onCheckedChanged = { onLocationSwitchChanged(it) },
+ label = stringResource(R.string.app_permission_location_accuracy),
+ toggleControl = ToggleChipToggleControl.Switch,
+ modifier = Modifier.fillMaxWidth(),
+ labelMaxLine = Integer.MAX_VALUE
+ )
+ }
+ }
+ for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) {
+ val pos: Int = BUTTON_RES_ID_TO_NUM.valueAt(i)
+ if (buttonVisibilities.value.size <= pos) {
+ // initial value of buttonVisibilities is empty
+ break
+ }
+ if (buttonVisibilities.value[pos]) {
+ item {
+ Chip(
+ label =
+ getPrimaryText(
+ pos,
+ locationVisibilities.value,
+ labelsByButton(BUTTON_RES_ID_TO_NUM.valueAt(i))
+ ),
+ onClick = { onButtonClicked(BUTTON_RES_ID_TO_NUM.keyAt(i)) },
+ modifier = Modifier.fillMaxWidth(),
+ labelMaxLines = Integer.MAX_VALUE
+ )
+ }
+ }
+ }
+ }
+}
+
+fun setContent(
+ composeView: ComposeView,
+ viewModel: WearGrantPermissionsViewModel,
+ onButtonClicked: (Int) -> Unit,
+ onLocationSwitchChanged: (Boolean) -> Unit
+) {
+ composeView.setContent {
+ WearGrantPermissionsScreen(viewModel, onButtonClicked, onLocationSwitchChanged)
+ }
+}
+
+@Composable
+internal fun labelsByButton(grantPermissionsButtonType: Int) =
+ when (grantPermissionsButtonType) {
+ ALLOW_BUTTON -> stringResource(R.string.grant_dialog_button_allow)
+ ALLOW_ALWAYS_BUTTON -> stringResource(R.string.grant_dialog_button_allow_always)
+ ALLOW_FOREGROUND_BUTTON -> stringResource(R.string.grant_dialog_button_allow_foreground)
+ DENY_BUTTON -> stringResource(R.string.grant_dialog_button_deny)
+ DENY_AND_DONT_ASK_AGAIN_BUTTON -> stringResource(R.string.grant_dialog_button_deny)
+ ALLOW_ONE_TIME_BUTTON -> stringResource(R.string.grant_dialog_button_allow_one_time)
+ NO_UPGRADE_BUTTON -> stringResource(R.string.grant_dialog_button_no_upgrade)
+ NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON ->
+ stringResource(R.string.grant_dialog_button_no_upgrade)
+ NO_UPGRADE_OT_BUTTON -> stringResource(R.string.grant_dialog_button_no_upgrade_one_time)
+ NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON ->
+ stringResource(R.string.grant_dialog_button_no_upgrade_one_time)
+ else -> ""
+ }
+
+@Composable
+private fun getPrimaryText(pos: Int, locationVisibilities: List<Boolean>, default: String): String {
+ val isPreciseLocation: Boolean =
+ locationVisibilities.getOrElse(LOCATION_ACCURACY_LAYOUT) { false } &&
+ locationVisibilities.getOrElse(DIALOG_WITH_FINE_LOCATION_ONLY) { false }
+ var res: String = default
+ if (pos == ALLOW_FOREGROUND_BUTTON && isPreciseLocation) {
+ res = stringResource(R.string.grant_dialog_button_change_to_precise_location)
+ }
+ if ((pos == DENY_BUTTON || pos == DENY_AND_DONT_ASK_AGAIN_BUTTON) && isPreciseLocation) {
+ res = stringResource(R.string.grant_dialog_button_keey_approximate_location)
+ }
+ return res
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt
new file mode 100644
index 000000000..1563f6a57
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.res.stringResource
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModel
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+
+@Composable
+fun WearManageCustomPermissionScreen(
+ viewModel: ManageCustomPermissionsViewModel,
+ onPermGroupClick: (String) -> Unit
+) {
+ val permissionGroups = viewModel.uiDataLiveData.observeAsState(emptyMap())
+ var isLoading by remember { mutableStateOf(true) }
+
+ WearManageCustomPermissionContent(
+ isLoading,
+ getPermGroupChipParams(permissionGroups.value),
+ onPermGroupClick
+ )
+
+ if (isLoading && permissionGroups.value.isNotEmpty()) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun WearManageCustomPermissionContent(
+ isLoading: Boolean,
+ permGroupChipParams: List<PermGroupChipParam>,
+ onPermGroupClick: (String) -> Unit
+) {
+ ScrollableScreen(
+ title = stringResource(R.string.additional_permissions),
+ isLoading = isLoading
+ ) {
+ for (params in permGroupChipParams) {
+ item {
+ Chip(
+ label = params.label,
+ labelMaxLines = 3,
+ icon = params.icon,
+ secondaryLabel = params.secondaryLabel,
+ secondaryLabelMaxLines = 3,
+ onClick = { onPermGroupClick(params.permGroupName) }
+ )
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionsFragment.kt
new file mode 100644
index 000000000..a9e83919f
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionsFragment.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.Constants
+import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment
+import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModel
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+
+class WearManageCustomPermissionsFragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val activity = requireActivity()
+ val application = activity.getApplication()
+ val sessionId: Long =
+ arguments?.getLong(Constants.EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID
+ val viewModel =
+ ViewModelProvider(
+ this,
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application)
+ )
+ .get(ManageCustomPermissionsViewModel::class.java)
+
+ val onPermGroupClick: (String) -> Unit = { permGroupName ->
+ viewModel.showPermissionApps(
+ this,
+ PermissionAppsFragment.createArgs(permGroupName, sessionId)
+ )
+ }
+
+ return ComposeView(activity).apply {
+ setContent {
+ WearPermissionTheme {
+ WearManageCustomPermissionScreen(viewModel, onPermGroupClick)
+ }
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt
new file mode 100644
index 000000000..bd1946759
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.graphics.drawable.Drawable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo
+import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupIcon
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel
+import com.android.permissioncontroller.permission.utils.StringUtils
+import com.android.permissioncontroller.permission.utils.Utils
+import java.text.Collator
+
+@Composable
+fun WearManageStandardPermissionScreen(
+ viewModel: ManageStandardPermissionsViewModel,
+ onPermGroupClick: (String) -> Unit,
+ onCustomPermissionsClick: () -> Unit,
+ onAutoRevokedClick: () -> Unit
+) {
+ val permissionGroups = viewModel.uiDataLiveData.observeAsState(emptyMap())
+ val numCustomPermGroups = viewModel.numCustomPermGroups.observeAsState(0)
+ val numAutoRevoked = viewModel.numAutoRevoked.observeAsState(0)
+ var isLoading by remember { mutableStateOf(true) }
+
+ WearManageStandardPermissionContent(
+ isLoading,
+ getPermGroupChipParams(permissionGroups.value),
+ numCustomPermGroups.value,
+ numAutoRevoked.value,
+ onPermGroupClick,
+ onCustomPermissionsClick,
+ onAutoRevokedClick
+ )
+
+ if (isLoading && permissionGroups.value.isNotEmpty()) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun getPermGroupChipParams(
+ permissionGroups: Map<String, PermGroupPackagesUiInfo?>
+): List<PermGroupChipParam> {
+ val context = LocalContext.current
+ val collator = Collator.getInstance(context.resources.getConfiguration().getLocales().get(0))
+ val summary =
+ if (context.resources.getBoolean(R.bool.config_useAlternativePermGroupSummary)) {
+ R.string.app_permissions_group_summary2
+ } else {
+ R.string.app_permissions_group_summary
+ }
+ return permissionGroups
+ // Removing Health Connect from the list of permissions to fix b/331260850
+ .filterNot { Utils.isHealthPermissionGroup(it.key) }
+ .mapNotNull {
+ val uiInfo = it.value ?: return@mapNotNull null
+ PermGroupChipParam(
+ permGroupName = it.key,
+ label = getPermGroupLabel(context, it.key).toString(),
+ icon = getPermGroupIcon(context, it.key),
+ secondaryLabel =
+ stringResource(summary, uiInfo.nonSystemGranted, uiInfo.nonSystemTotal)
+ )
+ }
+ .sortedWith { lhs, rhs -> collator.compare(lhs.label, rhs.label) }
+ .toList()
+}
+
+@Composable
+internal fun WearManageStandardPermissionContent(
+ isLoading: Boolean,
+ permGroupChipParams: List<PermGroupChipParam>,
+ numCustomPermGroups: Int,
+ numAutoRevoked: Int,
+ onPermGroupClick: (String) -> Unit,
+ onCustomPermissionsClick: () -> Unit,
+ onAutoRevokedClick: () -> Unit
+) {
+ ScrollableScreen(
+ title = stringResource(R.string.app_permission_manager),
+ isLoading = isLoading
+ ) {
+ for (params in permGroupChipParams) {
+ item {
+ Chip(
+ label = params.label,
+ labelMaxLines = 3,
+ icon = params.icon,
+ secondaryLabel = params.secondaryLabel,
+ secondaryLabelMaxLines = 3,
+ onClick = { onPermGroupClick(params.permGroupName) }
+ )
+ }
+ }
+
+ if (numCustomPermGroups > 0) {
+ item {
+ Chip(
+ label = stringResource(R.string.additional_permissions),
+ labelMaxLines = 3,
+ icon = R.drawable.ic_more_horizontal,
+ secondaryLabel =
+ StringUtils.getIcuPluralsString(
+ LocalContext.current,
+ R.string.additional_permissions_more,
+ numCustomPermGroups
+ ),
+ secondaryLabelMaxLines = 3,
+ onClick = onCustomPermissionsClick
+ )
+ }
+ }
+
+ if (numAutoRevoked > 0) {
+ item {
+ Chip(
+ label = stringResource(R.string.auto_revoke_permission_notification_title),
+ labelMaxLines = 3,
+ icon = R.drawable.ic_info,
+ secondaryLabel = stringResource(R.string.auto_revoke_setting_subtitle),
+ secondaryLabelMaxLines = 3,
+ onClick = onAutoRevokedClick
+ )
+ }
+ }
+ }
+}
+
+internal data class PermGroupChipParam(
+ val permGroupName: String,
+ val label: String,
+ val icon: Drawable?,
+ val secondaryLabel: String,
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionsFragment.kt
new file mode 100644
index 000000000..16ed1f067
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionsFragment.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.Constants
+import com.android.permissioncontroller.permission.ui.handheld.ManageCustomPermissionsFragment
+import com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment
+import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+
+class WearManageStandardPermissionsFragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val activity = requireActivity()
+ val application = activity.getApplication()
+ val sessionId: Long =
+ arguments?.getLong(Constants.EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID
+ val viewModel: ManageStandardPermissionsViewModel =
+ ViewModelProvider(
+ this,
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application)
+ )
+ .get(ManageStandardPermissionsViewModel::class.java)
+
+ val onPermGroupClick: (String) -> Unit = { permGroupName ->
+ viewModel.showPermissionApps(
+ this,
+ PermissionAppsFragment.createArgs(permGroupName, sessionId)
+ )
+ }
+ val onCustomPermGroupClick = {
+ viewModel.showCustomPermissions(
+ this,
+ ManageCustomPermissionsFragment.createArgs(sessionId)
+ )
+ }
+ val onAutoRevokeClick = {
+ viewModel.showAutoRevoke(this, WearUnusedAppsFragment.createArgs(sessionId))
+ }
+
+ return ComposeView(activity).apply {
+ setContent {
+ WearPermissionTheme {
+ WearManageStandardPermissionScreen(
+ viewModel,
+ onPermGroupClick,
+ onCustomPermGroupClick,
+ onAutoRevokeClick
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt
new file mode 100644
index 000000000..d8eb71e0e
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsFragment.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.Manifest
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.UserHandle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.Constants
+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.handheld.AppPermissionFragment
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModelFactory
+
+/**
+ * This is a condensed version of
+ * [com.android.permissioncontroller.permission.ui.handheld.PermissionAppsFragment], tailored for
+ * Wear.
+ *
+ * Show and manage apps which request a single permission group.
+ *
+ * <p>Shows a list of apps which request at least on permission of this group.
+ */
+class WearPermissionAppsFragment : Fragment(), PermissionsUsagesChangeCallback {
+ private val LOG_TAG = "PermissionAppsFragment"
+
+ private lateinit var permissionUsages: PermissionUsages
+ private lateinit var wearViewModel: WearAppPermissionUsagesViewModel
+
+ // Suppress warning of the deprecated class [android.app.LoaderManager] since other form factors
+ // are using the class to load PermissionUsages.
+ @Suppress("DEPRECATION")
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val permGroupName =
+ arguments?.getString(Intent.EXTRA_PERMISSION_GROUP_NAME)
+ ?: arguments?.getString(Intent.EXTRA_PERMISSION_NAME)
+ ?: throw RuntimeException("Permission group name must not be null.")
+ val sessionId: Long =
+ arguments?.getLong(Constants.EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID
+ val isStorageAndLessThanT =
+ !SdkLevel.isAtLeastT() && permGroupName == Manifest.permission_group.STORAGE
+
+ val activity = requireActivity()
+ val factory =
+ PermissionAppsViewModelFactory(activity.getApplication(), permGroupName, this, Bundle())
+ val viewModel = ViewModelProvider(this, factory).get(PermissionAppsViewModel::class.java)
+ wearViewModel =
+ ViewModelProvider(this, WearAppPermissionUsagesViewModelFactory())
+ .get(WearAppPermissionUsagesViewModel::class.java)
+
+ val onAppClick: (String, UserHandle, String) -> Unit = { packageName, user, category ->
+ run {
+ viewModel.navigateToAppPermission(
+ this,
+ packageName,
+ user,
+ AppPermissionFragment.createArgs(
+ packageName,
+ null,
+ permGroupName,
+ user,
+ this::class.java.name,
+ sessionId,
+ category
+ )
+ )
+ }
+ }
+
+ val onShowSystemClick: (Boolean) -> Unit = { showSystem ->
+ run { viewModel.updateShowSystem(showSystem) }
+ }
+
+ val logPermissionAppsFragmentCreated:
+ (String, UserHandle, Long, Boolean, Boolean, Boolean) -> Unit =
+ { packageName, user, viewId, isAllowed, isAllowedForeground, isDenied ->
+ run {
+ viewModel.logPermissionAppsFragmentCreated(
+ packageName,
+ user,
+ viewId,
+ isAllowed,
+ isAllowedForeground,
+ isDenied,
+ sessionId,
+ activity.getApplication(),
+ permGroupName,
+ LOG_TAG
+ )
+ }
+ }
+
+ // If the build type is below S, the app ops for permission usage can't be found. Thus, we
+ // shouldn't load permission usages, for them.
+ if (SdkLevel.isAtLeastS()) {
+ permissionUsages = PermissionUsages(requireContext())
+
+ val filterTimeBeginMillis: Long = viewModel.getFilterTimeBeginMillis()
+ permissionUsages.load(
+ null,
+ null,
+ filterTimeBeginMillis,
+ Long.MAX_VALUE,
+ PermissionUsages.USAGE_FLAG_LAST,
+ requireActivity().getLoaderManager(),
+ false,
+ false,
+ this,
+ false
+ )
+ }
+
+ return ComposeView(requireContext()).apply {
+ setContent {
+ WearPermissionAppsScreen(
+ WearPermissionAppsHelper(
+ activity.getApplication(),
+ permGroupName,
+ viewModel,
+ wearViewModel,
+ isStorageAndLessThanT,
+ onAppClick,
+ onShowSystemClick,
+ logPermissionAppsFragmentCreated
+ )
+ )
+ }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.S)
+ override fun onPermissionUsagesChanged() {
+ if (permissionUsages.usages.isEmpty()) {
+ return
+ }
+ if (context == null) {
+ // Async result has come in after our context is gone.
+ return
+ }
+ wearViewModel.appPermissionUsages.value =
+ ArrayList<AppPermissionUsage>(permissionUsages.usages)
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt
new file mode 100644
index 000000000..d14cdb620
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsHelper.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.app.Application
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.permission.flags.Flags
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
+import com.android.permissioncontroller.permission.ui.Category
+import com.android.permissioncontroller.permission.ui.model.PermissionAppsViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.WearAppPermissionUsagesViewModel
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupDescription
+import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel
+import com.android.settingslib.utils.applications.AppUtils
+import java.text.Collator
+import java.util.Random
+
+/** Helper class for WearPermissionsAppScreen. */
+class WearPermissionAppsHelper(
+ val application: Application,
+ val permGroupName: String,
+ val viewModel: PermissionAppsViewModel,
+ val wearViewModel: WearAppPermissionUsagesViewModel,
+ private val isStorageAndLessThanT: Boolean,
+ private val onAppClick: (String, UserHandle, String) -> Unit,
+ val onShowSystemClick: (Boolean) -> Unit,
+ val logFragmentCreated: (String, UserHandle, Long, Boolean, Boolean, Boolean) -> Unit
+) {
+ fun categorizedAppsLiveData() = viewModel.categorizedAppsLiveData
+ fun hasSystemAppsLiveData() = viewModel.hasSystemAppsLiveData
+ fun shouldShowSystemLiveData() = viewModel.shouldShowSystemLiveData
+ fun showAlways() = viewModel.showAllowAlwaysStringLiveData.value ?: false
+ fun getTitle() = getPermGroupLabel(application, permGroupName).toString()
+ fun getSubTitle() = getPermGroupDescription(application, permGroupName).toString()
+ fun getChipsByCategory(
+ categorizedApps: Map<Category, List<Pair<String, UserHandle>>>,
+ appPermissionUsages: List<AppPermissionUsage>
+ ): Map<String, List<ChipInfo>> {
+ val chipsByCategory: MutableMap<String, MutableList<ChipInfo>> = HashMap()
+
+ // A mapping of user + packageName to their last access timestamps for the permission group.
+ val groupUsageLastAccessTime: Map<String, Long> =
+ viewModel.extractGroupUsageLastAccessTime(appPermissionUsages)
+
+ val context = application
+ val collator = Collator.getInstance(context.resources.configuration.locales.get(0))
+ val comparator = ChipComparator(collator)
+
+ val viewIdForLogging = Random().nextLong()
+ for (category in Category.values()) {
+ if (category == Category.ALLOWED && isStorageAndLessThanT) {
+ val allowedList = categorizedApps[Category.ALLOWED]
+ if (!allowedList.isNullOrEmpty()) {
+ allowedList
+ .partition { p -> viewModel.packageHasFullStorage(p.first, p.second) }
+ .let {
+ if (it.first.isNotEmpty()) {
+ chipsByCategory[STORAGE_ALLOWED_FULL] =
+ convertToChips(
+ category,
+ it.first,
+ viewIdForLogging,
+ comparator,
+ groupUsageLastAccessTime
+ )
+ }
+ if (it.second.isNotEmpty()) {
+ chipsByCategory[STORAGE_ALLOWED_SCOPED] =
+ convertToChips(
+ category,
+ it.second,
+ viewIdForLogging,
+ comparator,
+ groupUsageLastAccessTime
+ )
+ }
+ }
+ }
+ continue
+ }
+ val list = categorizedApps[category]
+ if (!list.isNullOrEmpty()) {
+ chipsByCategory[category.categoryName] =
+ convertToChips(
+ category,
+ list,
+ viewIdForLogging,
+ comparator,
+ groupUsageLastAccessTime
+ )
+ }
+ }
+
+ // Add no_apps chips to allowed and denied if it doesn't have an app.
+ addNoAppsIfNeeded(chipsByCategory)
+ return chipsByCategory
+ }
+
+ private fun convertToChips(
+ category: Category,
+ list: List<Pair<String, UserHandle>>,
+ viewIdForLogging: Long,
+ comparator: Comparator<ChipInfo>,
+ groupUsageLastAccessTime: Map<String, Long>
+ ) =
+ list
+ .map { p ->
+ val lastAccessTime = groupUsageLastAccessTime[(p.second.toString() + p.first)]
+ createAppChipInfo(
+ application,
+ p.first,
+ p.second,
+ category,
+ onAppClick,
+ viewIdForLogging,
+ lastAccessTime
+ )
+ }
+ .sortedWith(comparator)
+ .toMutableList()
+
+ fun setCreationLogged(isLogged: Boolean) {
+ viewModel.creationLogged = isLogged
+ }
+
+ private fun createAppChipInfo(
+ application: Application,
+ packageName: String,
+ user: UserHandle,
+ category: Category,
+ onClick: (packageName: String, user: UserHandle, category: String) -> Unit,
+ viewIdForLogging: Long,
+ lastAccessTime: Long?
+ ): ChipInfo {
+ if (!viewModel.creationLogged) {
+ logFragmentCreated(
+ packageName,
+ user,
+ viewIdForLogging,
+ category == Category.ALLOWED,
+ category == Category.ALLOWED_FOREGROUND,
+ category == Category.DENIED
+ )
+ }
+ val summary =
+ if (Flags.wearPrivacyDashboardEnabledReadOnly()) {
+ lastAccessTime?.let { WearUtils.getPreferenceSummary(application, lastAccessTime) }
+ } else {
+ null
+ }
+ return ChipInfo(
+ title = KotlinUtils.getPackageLabel(application, packageName, user),
+ summary = summary,
+ contentDescription =
+ AppUtils.getAppContentDescription(application, packageName, user.getIdentifier()),
+ icon = KotlinUtils.getBadgedPackageIcon(application, packageName, user),
+ onClick = { onClick(packageName, user, category.categoryName) }
+ )
+ }
+
+ private fun addNoAppsIfNeeded(chipsByCategory: MutableMap<String, MutableList<ChipInfo>>) {
+ addNoAppsToAllowed(chipsByCategory)
+ addNoAppsToDenied(chipsByCategory)
+ }
+
+ private fun addNoAppsToAllowed(chipsByCategory: MutableMap<String, MutableList<ChipInfo>>) {
+ if (isStorageAndLessThanT) {
+ // For the storage permission,
+ // allowed category is split into allowed_full and allowed_scoped categories,
+ // add no_apps chip to the categories.
+ addNoAppsTo(chipsByCategory, STORAGE_ALLOWED_FULL, R.string.no_apps_allowed_full)
+ addNoAppsTo(chipsByCategory, STORAGE_ALLOWED_SCOPED, R.string.no_apps_allowed_scoped)
+ return
+ }
+ addNoAppsTo(chipsByCategory, Category.ALLOWED.categoryName, R.string.no_apps_allowed)
+ }
+
+ private fun addNoAppsToDenied(chipsByCategory: MutableMap<String, MutableList<ChipInfo>>) {
+ addNoAppsTo(chipsByCategory, Category.DENIED.categoryName, R.string.no_apps_denied)
+ }
+
+ private fun addNoAppsTo(
+ chipsByCategory: MutableMap<String, MutableList<ChipInfo>>,
+ categoryName: String,
+ titleResId: Int
+ ) {
+ if (chipsByCategory[categoryName].isNullOrEmpty()) {
+ chipsByCategory[categoryName] =
+ mutableListOf(
+ ChipInfo(title = application.resources.getString(titleResId), enabled = false)
+ )
+ }
+ }
+
+ companion object {
+ private const val STORAGE_ALLOWED_FULL = "allowed_storage_full"
+ private const val STORAGE_ALLOWED_SCOPED = "allowed_storage_scoped"
+ }
+}
+
+class ChipInfo(
+ val title: String,
+ val summary: String? = null,
+ val contentDescription: String? = null,
+ val onClick: () -> Unit = {},
+ val icon: Drawable? = null,
+ val enabled: Boolean = true
+)
+
+internal class ChipComparator(val collator: Collator) : Comparator<ChipInfo> {
+ override fun compare(lhs: ChipInfo, rhs: ChipInfo): Int {
+ var result = collator.compare(lhs.title, rhs.title)
+ if (result == 0) {
+ result = lhs.title.compareTo(rhs.title)
+ }
+ return result
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt
new file mode 100644
index 000000000..d694f20f8
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.Text
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.Category
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ListSubheader
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+
+/** Compose the screen associated to a [WearPermissionAppsFragment]. */
+@Composable
+fun WearPermissionAppsScreen(helper: WearPermissionAppsHelper) {
+ val categorizedApps = helper.categorizedAppsLiveData().observeAsState(emptyMap())
+ val hasSystemApps = helper.hasSystemAppsLiveData().observeAsState(false)
+ val showSystem = helper.shouldShowSystemLiveData().observeAsState(false)
+ val appPermissionUsages = helper.wearViewModel.appPermissionUsages.observeAsState(emptyList())
+ var isLoading by remember { mutableStateOf(true) }
+
+ val title = helper.getTitle()
+ val subTitle = helper.getSubTitle()
+ val showAlways = helper.showAlways()
+ val chipsByCategory =
+ helper.getChipsByCategory(categorizedApps.value, appPermissionUsages.value)
+
+ WearPermissionAppsContent(
+ chipsByCategory,
+ showSystem.value,
+ hasSystemApps.value,
+ title,
+ subTitle,
+ showAlways,
+ isLoading,
+ helper.onShowSystemClick
+ )
+
+ if (isLoading && categorizedApps.value.isNotEmpty()) {
+ isLoading = false
+ }
+ helper.setCreationLogged(true)
+}
+
+@Composable
+internal fun WearPermissionAppsContent(
+ chipsByCategory: Map<String, List<ChipInfo>>,
+ showSystem: Boolean,
+ hasSystemApps: Boolean,
+ title: String,
+ subtitle: String,
+ showAlways: Boolean,
+ isLoading: Boolean,
+ onShowSystemClick: (showSystem: Boolean) -> Unit
+) {
+ ScrollableScreen(title = title, subtitle = subtitle, isLoading = isLoading) {
+ val firstItemIndex = categoryOrder.indexOfFirst { !chipsByCategory[it].isNullOrEmpty() }
+ for ((index, category) in categoryOrder.withIndex()) {
+ val chips = chipsByCategory[category]
+ if (chips.isNullOrEmpty()) {
+ continue
+ }
+ item {
+ ListSubheader(
+ modifier =
+ Modifier.padding(
+ top = if (index == firstItemIndex) 0.dp else 12.dp,
+ bottom = 4.dp,
+ start = 14.dp,
+ end = 14.dp
+ )
+ ) {
+ Text(text = stringResource(getCategoryString(category, showAlways)))
+ }
+ }
+ chips.forEach {
+ item {
+ Chip(
+ label = it.title,
+ labelMaxLines = Int.MAX_VALUE,
+ secondaryLabel = it.summary,
+ secondaryLabelMaxLines = Int.MAX_VALUE,
+ icon = it.icon,
+ enabled = it.enabled,
+ onClick = { it.onClick() },
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ }
+ }
+
+ if (hasSystemApps) {
+ item {
+ Chip(
+ label =
+ if (showSystem) {
+ stringResource(R.string.menu_hide_system)
+ } else {
+ stringResource(R.string.menu_show_system)
+ },
+ labelMaxLines = Int.MAX_VALUE,
+ onClick = { onShowSystemClick(!showSystem) },
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ }
+ }
+}
+
+internal fun getCategoryString(category: String, showAlways: Boolean) =
+ when (category) {
+ "allowed_storage_full" -> R.string.allowed_storage_full
+ "allowed_storage_scoped" -> R.string.allowed_storage_scoped
+ Category.ALLOWED.categoryName ->
+ if (showAlways) {
+ R.string.allowed_always_header
+ } else {
+ R.string.allowed_header
+ }
+ Category.ALLOWED_FOREGROUND.categoryName -> R.string.allowed_foreground_header
+ Category.ASK.categoryName -> R.string.ask_header
+ Category.DENIED.categoryName -> R.string.denied_header
+ else -> throw IllegalArgumentException("Wrong category: $category")
+ }
+
+internal val categoryOrder =
+ listOf(
+ "allowed_storage_full",
+ "allowed_storage_scoped",
+ Category.ALLOWED.categoryName,
+ Category.ALLOWED_FOREGROUND.categoryName,
+ Category.ASK.categoryName,
+ Category.DENIED.categoryName
+ )
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt
new file mode 100644
index 000000000..ead7f9503
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsFragment.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsViewModelFactory
+import com.android.permissioncontroller.permission.utils.KotlinUtils.is7DayToggleEnabled
+
+/**
+ * This is a condensed version of
+ * [com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageDetailsFragment],
+ * tailored for Wear.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+class WearPermissionUsageDetailsFragment : Fragment() {
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val permissionGroup =
+ arguments?.getString(Intent.EXTRA_PERMISSION_GROUP_NAME)
+ ?: let {
+ Log.e(TAG, "No permission group was provided for PermissionDetailsFragment")
+ return null
+ }
+ val showSystem =
+ arguments?.getBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, false) ?: false
+ val show7Days =
+ arguments?.getBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, false) ?: false
+
+ val factory =
+ PermissionUsageDetailsViewModelFactory(
+ PermissionControllerApplication.get(),
+ this,
+ permissionGroup
+ )
+ val viewModel =
+ ViewModelProvider(this, factory).get(PermissionUsageDetailsViewModel::class.java)
+ viewModel.updateShowSystemAppsToggle(showSystem)
+ viewModel.updateShow7DaysToggle(is7DayToggleEnabled() && show7Days)
+
+ return ComposeView(requireContext()).apply {
+ setContent { WearPermissionUsageDetailsScreen(permissionGroup, viewModel) }
+ }
+ }
+
+ companion object {
+ private const val TAG = "WearPermissionUsageDetails"
+
+ @JvmStatic
+ fun newInstance(
+ groupName: String?,
+ showSystem: Boolean,
+ show7Days: Boolean
+ ): WearPermissionUsageDetailsFragment {
+ return WearPermissionUsageDetailsFragment().apply {
+ val arguments =
+ Bundle().apply {
+ if (groupName != null) {
+ putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName)
+ }
+ putBoolean(ManagePermissionsActivity.EXTRA_SHOW_SYSTEM, showSystem)
+ putBoolean(ManagePermissionsActivity.EXTRA_SHOW_7_DAYS, show7Days)
+ }
+ setArguments(arguments)
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt
new file mode 100644
index 000000000..3c62e5343
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.content.Intent
+import android.os.Build
+import android.text.format.DateFormat
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.MaterialTheme
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel
+import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+
+@RequiresApi(Build.VERSION_CODES.S)
+@Composable
+fun WearPermissionUsageDetailsScreen(
+ permissionGroup: String,
+ viewModel: PermissionUsageDetailsViewModel
+) {
+ val context = LocalContext.current
+ val uiData = viewModel.permissionUsagesDetailsInfoUiLiveData.observeAsState(null)
+ val showSystem = viewModel.showSystemLiveData.observeAsState(false)
+ var isLoading by remember { mutableStateOf(true) }
+
+ val title = stringResource(R.string.permission_history_title)
+ val subtitle =
+ stringResource(
+ R.string.permission_group_usage_title,
+ KotlinUtils.getPermGroupLabel(context, permissionGroup)
+ )
+ val hasSystemApps: Boolean = uiData.value?.containsSystemAppAccesses ?: false
+ val onShowSystemClick: (Boolean) -> Unit = { show ->
+ run { viewModel.updateShowSystemAppsToggle(show) }
+ }
+ val onChipClick: (AppPermissionAccessUiInfo) -> Unit = { uiInfo ->
+ run {
+ val intent =
+ PermissionUsageDetailsViewModel.createHistoryPreferenceClickIntent(
+ context,
+ uiInfo.userHandle,
+ uiInfo.packageName,
+ uiInfo.permissionGroup,
+ uiInfo.accessStartTime,
+ uiInfo.accessEndTime,
+ uiInfo.showingAttribution,
+ uiInfo.attributionTags
+ )
+ context.startActivityAsUser(intent, uiInfo.userHandle)
+ }
+ }
+ val onManagePermissionClick: () -> Unit = {
+ val intent: Intent =
+ Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
+ .putExtra(Intent.EXTRA_PERMISSION_NAME, permissionGroup)
+ context.startActivity(intent)
+ }
+
+ val appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo> =
+ uiData.value?.appPermissionAccessUiInfoList ?: emptyList()
+
+ WearPermissionUsageDetailsContent(
+ title,
+ subtitle,
+ isLoading,
+ hasSystemApps,
+ showSystem.value,
+ onShowSystemClick,
+ appPermissionAccessUiInfoList,
+ onChipClick,
+ onManagePermissionClick
+ )
+
+ if (isLoading && uiData.value != null) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun WearPermissionUsageDetailsContent(
+ title: String,
+ subtitle: String,
+ isLoading: Boolean,
+ hasSystemApps: Boolean,
+ showSystem: Boolean,
+ onShowSystemClick: (Boolean) -> Unit,
+ appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo>,
+ onChipClick: (AppPermissionAccessUiInfo) -> Unit,
+ onManagePermissionClick: () -> Unit
+) {
+ ScrollableScreen(title = title, subtitle = subtitle, isLoading = isLoading) {
+ if (appPermissionAccessUiInfoList.isEmpty()) {
+ item { Chip(label = stringResource(R.string.no_apps), onClick = {}) }
+ } else {
+ for (uiInfo in appPermissionAccessUiInfoList) {
+ item {
+ Chip(
+ label = uiInfo.packageLabel,
+ labelMaxLines = Int.MAX_VALUE,
+ secondaryLabel =
+ DateFormat.getTimeFormat(LocalContext.current)
+ .format(uiInfo.accessEndTime),
+ secondaryLabelMaxLines = Int.MAX_VALUE,
+ icon = uiInfo.badgedPackageIcon,
+ onClick = { onChipClick(uiInfo) }
+ )
+ }
+ }
+ if (hasSystemApps) {
+ item {
+ Chip(
+ label =
+ if (showSystem) {
+ stringResource(R.string.menu_hide_system)
+ } else {
+ stringResource(R.string.menu_show_system)
+ },
+ labelMaxLines = Int.MAX_VALUE,
+ onClick = { onShowSystemClick(!showSystem) },
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ }
+ item {
+ Chip(
+ label = stringResource(R.string.manage_permission),
+ textColor = MaterialTheme.colors.background,
+ colors = ChipDefaults.primaryChipColors(),
+ onClick = { onManagePermissionClick() },
+ )
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.kt
new file mode 100644
index 000000000..8f8960269
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageFragment.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.permissioncontroller.permission.ui.wear
+
+import android.os.Build
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.Constants
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionUsageViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionUsageViewModelFactory
+
+/**
+ * This is a condensed version of
+ * [com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageFragment], tailored
+ * for Wear.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+class WearPermissionUsageFragment : Fragment() {
+ lateinit var wearViewModel: WearPermissionUsageViewModel
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val sessionId: Long =
+ arguments?.getLong(Constants.EXTRA_SESSION_ID) ?: Constants.INVALID_SESSION_ID
+ val factory =
+ PermissionUsageViewModelFactory(requireActivity().getApplication(), this, Bundle())
+ val viewModel = ViewModelProvider(this, factory).get(PermissionUsageViewModel::class.java)
+ wearViewModel =
+ ViewModelProvider(this, WearPermissionUsageViewModelFactory(viewModel))
+ .get(WearPermissionUsageViewModel::class.java)
+
+ viewModel.permissionUsagesUiLiveData.observe(
+ this,
+ wearViewModel::updatePermissionUsagesUiStateLiveData
+ )
+ return ComposeView(requireContext()).apply {
+ setContent { WearPermissionUsageScreen(sessionId, viewModel, wearViewModel) }
+ }
+ }
+
+ companion object {
+ @JvmStatic
+ fun newInstance(sessionId: Long): WearPermissionUsageFragment {
+ return WearPermissionUsageFragment().apply {
+ val arguments = Bundle().apply { putLong(Constants.EXTRA_SESSION_ID, sessionId) }
+ setArguments(arguments)
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt
new file mode 100644
index 000000000..62d0c6212
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageControlPreference
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.model.WearPermissionUsageViewModel
+import com.android.permissioncontroller.permission.utils.Utils
+import java.text.Collator
+
+@RequiresApi(Build.VERSION_CODES.S)
+@Composable
+fun WearPermissionUsageScreen(
+ sessionId: Long,
+ viewModel: PermissionUsageViewModel,
+ wearViewModel: WearPermissionUsageViewModel
+) {
+ val context = LocalContext.current
+ val permissionUsagesUiData = wearViewModel.permissionUsagesUiStateLiveData.observeAsState(null)
+ val showSystem = wearViewModel.showSystemAppsLiveData.observeAsState(false)
+ val show7Days = wearViewModel.show7DaysLiveData.observeAsState(false)
+ var isLoading by remember { mutableStateOf(true) }
+ val isDataLoaded = permissionUsagesUiData.value is PermissionUsagesUiState.Success
+ val hasSystemApps: Boolean =
+ if (isDataLoaded) {
+ val uiState = permissionUsagesUiData.value as PermissionUsagesUiState.Success
+ uiState.shouldShowSystemToggle
+ } else {
+ false
+ }
+ val onShowSystemClick: (Boolean) -> Unit = { show ->
+ run {
+ wearViewModel.updatePermissionUsagesUiStateLiveData(viewModel.updateShowSystem(show))
+ wearViewModel.showSystemAppsLiveData.value = viewModel.getShowSystemApps()
+ }
+ }
+
+ val permissionGroupWithUsageCounts: Map<String, Int> =
+ if (isDataLoaded) {
+ val uiState = permissionUsagesUiData.value as PermissionUsagesUiState.Success
+ uiState.permissionGroupUsageCount
+ } else {
+ emptyMap()
+ }
+ val permissionGroupWithUsageCountsEntries: List<Map.Entry<String, Int>> =
+ ArrayList<Map.Entry<String, Int>>(permissionGroupWithUsageCounts.entries)
+
+ val collator = Collator.getInstance(context.resources.configuration.locales.get(0))
+ val permissionGroupPreferences =
+ permissionGroupWithUsageCountsEntries
+ // Removing Health Connect from the list of permissions to fix b/331260850
+ .filterNot { Utils.isHealthPermissionGroup(it.key) }
+ .map {
+ PermissionUsageControlPreference(
+ context,
+ it.key,
+ it.value,
+ showSystem.value,
+ sessionId,
+ show7Days.value
+ )
+ }
+ .sortedWith { o1, o2 ->
+ var result = collator.compare(o1.title.toString(), o2.title.toString())
+ if (result == 0) {
+ result = o1.title.toString().compareTo(o2.title.toString())
+ }
+ result
+ }
+ .toList()
+
+ WearPermissionUsageContent(
+ isLoading,
+ hasSystemApps,
+ showSystem.value,
+ onShowSystemClick,
+ permissionGroupPreferences
+ )
+
+ if (isLoading && isDataLoaded) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun WearPermissionUsageContent(
+ isLoading: Boolean,
+ hasSystemApps: Boolean,
+ showSystem: Boolean,
+ onShowSystemClick: (Boolean) -> Unit,
+ permissionGroupPreferences: List<PermissionUsageControlPreference>
+) {
+ ScrollableScreen(
+ title = stringResource(R.string.permission_usage_title),
+ isLoading = isLoading
+ ) {
+ if (permissionGroupPreferences.isEmpty()) {
+ item { Chip(label = stringResource(R.string.no_permissions), onClick = {}) }
+ } else {
+ for (preference in permissionGroupPreferences) {
+ item {
+ Chip(
+ label = preference.title.toString(),
+ labelMaxLines = Int.MAX_VALUE,
+ secondaryLabel = preference.summary.toString(),
+ secondaryLabelMaxLines = Int.MAX_VALUE,
+ icon = preference.icon,
+ enabled = preference.isEnabled,
+ onClick = { preference.performClick() }
+ )
+ }
+ }
+ if (hasSystemApps) {
+ item {
+ Chip(
+ label =
+ if (showSystem) {
+ stringResource(R.string.menu_hide_system)
+ } else {
+ stringResource(R.string.menu_show_system)
+ },
+ labelMaxLines = Int.MAX_VALUE,
+ onClick = { onShowSystemClick(!showSystem) },
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsFragment.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsFragment.kt
new file mode 100644
index 000000000..64acfdd96
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsFragment.kt
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.Manifest
+import android.content.Context
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.UserHandle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
+import com.android.permissioncontroller.Constants.INVALID_SESSION_ID
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPackageInfo
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod.Companion.allPeriods
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModelFactory
+import com.android.permissioncontroller.permission.ui.wear.model.WearUnusedAppsViewModel
+import com.android.permissioncontroller.permission.ui.wear.model.WearUnusedAppsViewModel.UnusedAppChip
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+import com.android.permissioncontroller.permission.utils.KotlinUtils
+import com.android.settingslib.utils.applications.AppUtils
+import java.text.Collator
+
+/**
+ * This is a condensed version of
+ * [com.android.permissioncontroller.permission.ui.UnusedAppsFragment.kt], tailored for Wear.
+ *
+ * A fragment displaying all applications that are unused as well as the option to remove them and
+ * to open them.
+ */
+class WearUnusedAppsFragment : Fragment() {
+ private lateinit var activity: FragmentActivity
+ private lateinit var context: Context
+ private lateinit var viewModel: UnusedAppsViewModel
+ private lateinit var wearViewModel: WearUnusedAppsViewModel
+ private lateinit var collator: Collator
+ private var sessionId: Long = 0L
+ private var isFirstLoad = false
+ private var categoryVisibilities: MutableList<Boolean> =
+ MutableList(UnusedPeriod.values().size) { false }
+ private var unusedAppsMap: MutableMap<UnusedPeriod, MutableMap<String, UnusedAppChip>> =
+ initUnusedAppsMap()
+
+ companion object {
+ private const val SHOW_LOAD_DELAY_MS = 200L
+ private val LOG_TAG = WearUnusedAppsFragment::class.java.simpleName
+
+ /**
+ * Create the args needed for this fragment
+ *
+ * @param sessionId The current session Id
+ * @return A bundle containing the session Id
+ */
+ @JvmStatic
+ fun createArgs(sessionId: Long): Bundle {
+ val bundle = Bundle()
+ bundle.putLong(EXTRA_SESSION_ID, sessionId)
+ return bundle
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ isFirstLoad = true
+ context = requireContext()
+ collator =
+ Collator.getInstance(context.getResources().getConfiguration().getLocales().get(0))
+ activity = requireActivity()
+ val application = activity.getApplication()
+ sessionId = arguments!!.getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID)
+ val factory = UnusedAppsViewModelFactory(activity.application, sessionId)
+ viewModel = ViewModelProvider(this, factory).get(UnusedAppsViewModel::class.java)
+ wearViewModel =
+ ViewModelProvider(
+ this,
+ ViewModelProvider.AndroidViewModelFactory.getInstance(application)
+ )
+ .get(WearUnusedAppsViewModel::class.java)
+ viewModel.unusedPackageCategoriesLiveData.observe(
+ this,
+ Observer {
+ it?.let { pkgs ->
+ updatePackages(pkgs)
+ updateWearViewModel(false)
+ }
+ }
+ )
+
+ if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) {
+ val handler = Handler(Looper.getMainLooper())
+ handler.postDelayed(
+ {
+ if (!viewModel.unusedPackageCategoriesLiveData.isInitialized) {
+ wearViewModel.loadingLiveData.value = true
+ } else {
+ updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!)
+ updateWearViewModel(false)
+ }
+ },
+ SHOW_LOAD_DELAY_MS
+ )
+ } else {
+ updatePackages(viewModel.unusedPackageCategoriesLiveData.value!!)
+ updateWearViewModel(false)
+ }
+
+ return ComposeView(activity).apply {
+ setContent { WearPermissionTheme { WearUnusedAppsScreen(wearViewModel) } }
+ }
+ }
+
+ private fun initUnusedAppsMap(): MutableMap<UnusedPeriod, MutableMap<String, UnusedAppChip>> {
+ val res = mutableMapOf<UnusedPeriod, MutableMap<String, UnusedAppChip>>()
+ for (period in allPeriods) {
+ res.put(period, mutableMapOf())
+ }
+ return res
+ }
+
+ private fun updatePackages(categorizedPackages: Map<UnusedPeriod, List<UnusedPackageInfo>>) {
+ // Remove stale unused app chips
+ for (period in allPeriods) {
+ val unUsedAppsInAPeriod = unusedAppsMap[period] ?: continue
+ val categorizedPackagesOfAPeriod = categorizedPackages[period]
+ if (categorizedPackagesOfAPeriod == null) {
+ unUsedAppsInAPeriod.clear()
+ continue
+ }
+ val categorizedPackageKeys =
+ categorizedPackagesOfAPeriod.map { createKey(it.packageName, it.user) }
+ // Do not remove apps that are still in the unused category
+ val keysToRemove = unUsedAppsInAPeriod.keys.filterNot { it in categorizedPackageKeys }
+ for (key in keysToRemove) {
+ unUsedAppsInAPeriod.remove(key)
+ }
+ }
+
+ var allCategoriesEmpty = true
+ for ((period, packages) in categorizedPackages) {
+ categoryVisibilities.set(periodToIndex(period), packages.isNotEmpty())
+ if (packages.isNotEmpty()) {
+ allCategoriesEmpty = false
+ }
+
+ for ((pkgName, user, _, permSet) in packages) {
+ val revokedPerms = permSet.toList()
+ val key = createKey(pkgName, user)
+
+ if (!unusedAppsMap[period]!!.containsKey(key)) {
+ val mostImportant = getMostImportantGroup(revokedPerms)
+ val importantLabel = KotlinUtils.getPermGroupLabel(context, mostImportant)
+ val 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
+ )
+ }
+ else ->
+ getString(
+ R.string.auto_revoked_app_summary_many,
+ importantLabel,
+ "${revokedPerms.size - 1}"
+ )
+ }
+
+ val onChipClicked: () -> Unit = {
+ run { viewModel.navigateToAppInfo(pkgName, user, sessionId) }
+ }
+
+ val chip =
+ UnusedAppChip(
+ KotlinUtils.getPackageLabel(activity.application, pkgName, user),
+ summary,
+ KotlinUtils.getBadgedPackageIcon(activity.application, pkgName, user),
+ AppUtils.getAppContentDescription(
+ context,
+ pkgName,
+ user.getIdentifier()
+ ),
+ onChipClicked
+ )
+ unusedAppsMap[period]!!.put(key, chip)
+ }
+ }
+
+ // Sort the chips
+ unusedAppsMap[period] =
+ unusedAppsMap[period]!!
+ .toList()
+ .sortedWith(Comparator { lhs, rhs -> compareUnusedApps(lhs, rhs) })
+ .toMap()
+ .toMutableMap()
+ }
+
+ wearViewModel.infoMsgCategoryVisibilityLiveData.value = !allCategoriesEmpty
+
+ if (isFirstLoad) {
+ if (categorizedPackages.any { (_, packages) -> packages.isNotEmpty() }) {
+ isFirstLoad = false
+ }
+ Log.i(LOG_TAG, "sessionId: $sessionId Showed Auto Revoke Page")
+ for (period in allPeriods) {
+ 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
+ )
+ }
+ }
+ }
+ }
+ }
+
+ private fun createKey(packageName: String, user: UserHandle): String {
+ return "$packageName:${user.identifier}"
+ }
+
+ private fun periodToIndex(period: UnusedPeriod): Int {
+ when (period) {
+ UnusedPeriod.ONE_MONTH -> return 0
+ UnusedPeriod.THREE_MONTHS -> return 1
+ UnusedPeriod.SIX_MONTHS -> return 2
+ }
+ }
+
+ private fun getMostImportantGroup(groupNames: List<String>): String {
+ return when {
+ groupNames.contains(Manifest.permission_group.LOCATION) ->
+ Manifest.permission_group.LOCATION
+ groupNames.contains(Manifest.permission_group.MICROPHONE) ->
+ Manifest.permission_group.MICROPHONE
+ groupNames.contains(Manifest.permission_group.CAMERA) ->
+ Manifest.permission_group.CAMERA
+ groupNames.contains(Manifest.permission_group.CONTACTS) ->
+ Manifest.permission_group.CONTACTS
+ groupNames.contains(Manifest.permission_group.STORAGE) ->
+ Manifest.permission_group.STORAGE
+ groupNames.contains(Manifest.permission_group.CALENDAR) ->
+ Manifest.permission_group.CALENDAR
+ groupNames.isNotEmpty() -> groupNames[0]
+ else -> ""
+ }
+ }
+
+ private fun compareUnusedApps(
+ lhs: Pair<String, UnusedAppChip>,
+ rhs: Pair<String, UnusedAppChip>
+ ): Int {
+ var result = collator.compare(lhs.second.label, rhs.second.label)
+ if (result == 0) {
+ result = collator.compare(lhs.first, rhs.first)
+ }
+ return result
+ }
+
+ private fun updateWearViewModel(isLoading: Boolean) {
+ wearViewModel.loadingLiveData.value = isLoading
+ wearViewModel.unusedPeriodCategoryVisibilitiesLiveData.setValue(categoryVisibilities)
+
+ // Need to copy to non mutable maps or compose will not update correctly
+ val map = mutableMapOf<UnusedPeriod, Map<String, UnusedAppChip>>()
+ for (period in allPeriods) {
+ map.put(period, unusedAppsMap[period]!!.toMap())
+ }
+
+ wearViewModel.unusedAppChipsLiveData.setValue(map.toMap())
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt
new file mode 100644
index 000000000..423fa7759
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.icu.text.MessageFormat
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.res.stringResource
+import androidx.wear.compose.material.Text
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.hibernation.isHibernationEnabled
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod.Companion.allPeriods
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.Icon
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.model.WearUnusedAppsViewModel
+
+@Composable
+fun WearUnusedAppsScreen(viewModel: WearUnusedAppsViewModel) {
+ val loading = viewModel.loadingLiveData.observeAsState(true)
+ val unusedPeriodCategoryVisibilities =
+ viewModel.unusedPeriodCategoryVisibilitiesLiveData.observeAsState(emptyList())
+ val infoMsgCategoryVisibility =
+ viewModel.infoMsgCategoryVisibilityLiveData.observeAsState(false)
+ val unusedAppChips = viewModel.unusedAppChipsLiveData.observeAsState(mapOf())
+
+ ScrollableScreen(
+ showTimeText = true,
+ title = getScreenTitle(),
+ isLoading = loading.value,
+ subtitle = getSubTitle(!infoMsgCategoryVisibility.value)
+ ) {
+ for (period in allPeriods) {
+ if (!unusedAppChips.value.containsKey(period)) {
+ continue
+ }
+ item {
+ val pos = posByPeriod(period)
+ if (unusedPeriodCategoryVisibilities.value.getOrElse(pos) { false }) {
+ Text(text = categoryTitleByPeriod(period))
+ }
+ }
+ for (unusedAppChip in unusedAppChips.value[period]!!.values) {
+ item {
+ Chip(
+ label = unusedAppChip.label,
+ secondaryLabel = unusedAppChip.summary,
+ icon = unusedAppChip.icon,
+ iconContentDescription = unusedAppChip.contentDescription,
+ onClick = unusedAppChip.onClick
+ )
+ }
+ }
+ }
+ // For info_msg_category
+ if (infoMsgCategoryVisibility.value) {
+ item { Icon(icon = R.drawable.ic_info_outline, contentDescription = null) }
+ if (isHibernationEnabled()) {
+ item { Text(text = stringResource(R.string.unused_apps_page_summary)) }
+ } else {
+ item { Text(text = stringResource(R.string.auto_revoked_apps_page_summary)) }
+ item { Text(text = stringResource(R.string.auto_revoke_open_app_message)) }
+ }
+ }
+ }
+}
+
+@Composable
+private fun getScreenTitle() =
+ if (isHibernationEnabled()) {
+ stringResource(R.string.unused_apps_page_title)
+ } else {
+ stringResource(R.string.permission_removed_page_title)
+ }
+
+@Composable
+private fun getSubTitle(shouldShow: Boolean) =
+ if (shouldShow) {
+ stringResource(R.string.no_unused_apps)
+ } else {
+ null
+ }
+
+@Composable
+private fun posByPeriod(period: UnusedPeriod) =
+ when (period) {
+ UnusedPeriod.ONE_MONTH -> 0
+ UnusedPeriod.THREE_MONTHS -> 1
+ UnusedPeriod.SIX_MONTHS -> 2
+ }
+
+@Composable
+private fun categoryTitleByPeriod(period: UnusedPeriod) =
+ MessageFormat.format(
+ stringResource(R.string.last_opened_category_title),
+ mapOf("count" to period.months)
+ )
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUtils.kt
new file mode 100644
index 000000000..7e4e939b9
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUtils.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear
+
+import android.content.Context
+import android.text.format.DateFormat
+import androidx.annotation.IntDef
+import com.android.permissioncontroller.R
+import java.time.ZonedDateTime
+import java.time.temporal.ChronoUnit
+import java.util.Locale
+
+object WearUtils {
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(value = [LAST_24H_TODAY, LAST_24H_YESTERDAY, LAST_7D, NOT_IN_LAST_7D])
+ annotation class AppPermsLastAccessType
+
+ const val LAST_24H_TODAY = 1
+ const val LAST_24H_YESTERDAY = 2
+ const val LAST_7D = 3
+ const val NOT_IN_LAST_7D = 4
+
+ /** Get the preference summary in app permission groups and permission apps screens for Wear. */
+ @JvmStatic
+ fun getPreferenceSummary(context: Context, lastAccessTime: Long?): String {
+ val summaryTimestamp = getPermissionLastAccessSummaryTimestamp(lastAccessTime, context)
+ val res = context.resources
+ return when (summaryTimestamp.second) {
+ LAST_24H_TODAY ->
+ res.getString(R.string.wear_app_perms_24h_access, summaryTimestamp.first)
+ LAST_24H_YESTERDAY ->
+ res.getString(R.string.wear_app_perms_24h_access_yest, summaryTimestamp.first)
+ LAST_7D ->
+ res.getString(
+ R.string.wear_app_perms_7d_access,
+ summaryTimestamp.third,
+ summaryTimestamp.first
+ )
+ else -> ""
+ }
+ }
+
+ @JvmStatic
+ private fun getPermissionLastAccessSummaryTimestamp(
+ lastAccessTime: Long?,
+ context: Context
+ ): Triple<String, Int, String> {
+ val midnightToday =
+ (ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).toEpochSecond() * 1000L)
+ val midnightYesterday =
+ ZonedDateTime.now().minusDays(1).truncatedTo(ChronoUnit.DAYS).toEpochSecond() * 1000L
+ val isLastAccessToday = (lastAccessTime != null && midnightToday <= lastAccessTime)
+ val isLastAccessTodayOrYesterday =
+ (lastAccessTime != null && midnightYesterday <= lastAccessTime)
+ var lastAccessTimeFormatted = ""
+ var lastAccessDateFormatted = ""
+ @AppPermsLastAccessType var lastAccessType = NOT_IN_LAST_7D
+ if (lastAccessTime != null) {
+ lastAccessTimeFormatted = DateFormat.getTimeFormat(context).format(lastAccessTime)
+ lastAccessDateFormatted = DateFormat.getDateFormat(context).format(lastAccessTime)
+ lastAccessType =
+ if (isLastAccessToday) LAST_24H_TODAY
+ else if (isLastAccessTodayOrYesterday) LAST_24H_YESTERDAY else LAST_7D
+ }
+ return Triple(lastAccessTimeFormatted, lastAccessType, lastAccessDateFormatted)
+ }
+
+ fun String.capitalize(): String = replaceFirstChar {
+ if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt
new file mode 100644
index 000000000..aa04cfad8
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.rememberTextMeasurer
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.LocalTextStyle
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.dialog.Alert
+import androidx.wear.compose.material.dialog.Dialog
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberColumnState
+
+/**
+ * This component is an alternative to [AlertContent], providing the following:
+ * - a convenient way of passing a title and a message;
+ * - additional content can be specified between the message and the buttons
+ * - default positive and negative buttons;
+ * - wrapped in a [Dialog];
+ */
+@Composable
+fun AlertDialog(
+ message: String,
+ iconRes: Int? = null,
+ onCancelButtonClick: () -> Unit,
+ onOKButtonClick: () -> Unit,
+ showDialog: Boolean,
+ scalingLazyListState: ScalingLazyListState,
+ modifier: Modifier = Modifier,
+ title: String? = null,
+ okButtonContentDescription: String = stringResource(android.R.string.ok),
+ cancelButtonContentDescription: String = stringResource(android.R.string.cancel)
+) {
+ Dialog(
+ showDialog = showDialog,
+ onDismissRequest = onCancelButtonClick,
+ scrollState = scalingLazyListState,
+ modifier = modifier
+ ) {
+ AlertContent(
+ title = title,
+ icon = { AlertIcon(iconRes) },
+ message = message,
+ onCancel = onCancelButtonClick,
+ onOk = onOKButtonClick,
+ okButtonContentDescription = okButtonContentDescription,
+ cancelButtonContentDescription = cancelButtonContentDescription
+ )
+ }
+}
+
+/**
+ * This component is an alternative to [Alert], providing the following:
+ * - a convenient way of passing a title and a message;
+ * - default one button;
+ * - wrapped in a [Dialog];
+ */
+@Composable
+fun SingleButtonAlertDialog(
+ message: String,
+ iconRes: Int? = null,
+ onButtonClick: () -> Unit,
+ showDialog: Boolean,
+ scalingLazyListState: ScalingLazyListState,
+ modifier: Modifier = Modifier,
+ title: String? = null,
+ buttonContentDescription: String = stringResource(android.R.string.ok)
+) {
+ Dialog(
+ showDialog = showDialog,
+ onDismissRequest = {},
+ scrollState = scalingLazyListState,
+ modifier = modifier
+ ) {
+ AlertContent(
+ title = title,
+ icon = { AlertIcon(iconRes) },
+ message = message,
+ onOk = onButtonClick,
+ okButtonContentDescription = buttonContentDescription
+ )
+ }
+}
+
+@Composable
+public fun AlertContent(
+ onCancel: (() -> Unit)? = null,
+ onOk: (() -> Unit)? = null,
+ icon: @Composable (() -> Unit)? = null,
+ title: String? = null,
+ message: String? = null,
+ okButtonContentDescription: String = stringResource(android.R.string.ok),
+ cancelButtonContentDescription: String = stringResource(android.R.string.cancel),
+ state: ScalingLazyColumnState =
+ rememberColumnState(
+ ScalingLazyColumnDefaults.responsive(
+ additionalPaddingAtBottom = 0.dp,
+ ),
+ ),
+ showPositionIndicator: Boolean = true,
+ content: (ScalingLazyListScope.() -> Unit)? = null,
+) {
+ val density = LocalDensity.current
+ val maxScreenWidthPx = with(density) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
+
+ ResponsiveDialogContent(
+ icon = icon,
+ title =
+ title?.let {
+ {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = it,
+ color = MaterialTheme.colors.onBackground,
+ textAlign = TextAlign.Center,
+ maxLines = if (icon == null) 3 else 2,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ },
+ message =
+ message?.let {
+ {
+ // Should message be start or center aligned?
+ val textMeasurer = rememberTextMeasurer()
+ val textStyle = LocalTextStyle.current
+ val totalPaddingPercentage =
+ globalHorizontalPadding + messageExtraHorizontalPadding
+ val lineCount =
+ remember(it, density, textStyle, textMeasurer) {
+ textMeasurer
+ .measure(
+ text = it,
+ style = textStyle,
+ constraints =
+ Constraints(
+ // Available width is reduced by responsive dialog
+ // horizontal
+ // padding.
+ maxWidth =
+ (maxScreenWidthPx *
+ (1f - totalPaddingPercentage * 2f / 100f))
+ .toInt(),
+ ),
+ )
+ .lineCount
+ }
+ val textAlign = if (lineCount <= 3) TextAlign.Center else TextAlign.Start
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = it,
+ color = MaterialTheme.colors.onBackground,
+ textAlign = textAlign,
+ )
+ }
+ },
+ content = content,
+ onOk = onOk,
+ onCancel = onCancel,
+ okButtonContentDescription = okButtonContentDescription,
+ cancelButtonContentDescription = cancelButtonContentDescription,
+ state = state,
+ showPositionIndicator = showPositionIndicator,
+ )
+}
+
+@Composable
+private fun AlertIcon(iconRes: Int?) =
+ if (iconRes != null && iconRes != 0) {
+ Icon(painter = painterResource(iconRes), contentDescription = null)
+ } else {
+ null
+ }
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
new file mode 100644
index 000000000..bcdf3b661
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AnnotatedText.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import android.text.Spanned
+import android.text.style.ClickableSpan
+import android.view.View
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.LinkAnnotation
+import androidx.compose.ui.text.LinkInteractionListener
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.wear.compose.material.MaterialTheme
+
+const val CLICKABLE_SPAN_TAG = "CLICKABLE_SPAN_TAG"
+
+@Composable
+fun AnnotatedText(text: CharSequence, style: TextStyle, modifier: Modifier = Modifier) {
+ val onClickCallbacks = mutableMapOf<String, (View) -> Unit>()
+ val context = LocalContext.current
+ val listener = LinkInteractionListener {
+ if (it is LinkAnnotation.Clickable) {
+ onClickCallbacks.get(it.tag)?.invoke(View(context))
+ }
+ }
+
+ val annotatedString =
+ spannableStringToAnnotatedString(text, onClickCallbacks, listener = listener)
+ BasicText(text = annotatedString, style = style, modifier = modifier)
+}
+
+@Composable
+private fun spannableStringToAnnotatedString(
+ text: CharSequence,
+ onClickCallbacks: MutableMap<String, (View) -> Unit>,
+ spanColor: Color = MaterialTheme.colors.primary,
+ listener: LinkInteractionListener
+) =
+ if (text is Spanned) {
+ buildAnnotatedString {
+ append((text.toString()))
+ for (span in text.getSpans(0, text.length, Any::class.java)) {
+ val start = text.getSpanStart(span)
+ val end = text.getSpanEnd(span)
+ when (span) {
+ is ClickableSpan ->
+ addClickableSpan(span, spanColor, start, end, onClickCallbacks, listener)
+ else -> addStyle(SpanStyle(), start, end)
+ }
+ }
+ }
+ } else {
+ AnnotatedString(text.toString())
+ }
+
+private fun AnnotatedString.Builder.addClickableSpan(
+ span: ClickableSpan,
+ spanColor: Color,
+ start: Int,
+ end: Int,
+ onClickCallbacks: MutableMap<String, (View) -> Unit>,
+ listener: LinkInteractionListener
+) {
+ val key = "${CLICKABLE_SPAN_TAG}:$start:$end"
+ onClickCallbacks[key] = span::onClick
+ addLink(LinkAnnotation.Clickable(key, linkInteractionListener = listener), start, end)
+ addStyle(
+ SpanStyle(color = spanColor, textDecoration = TextDecoration.Underline),
+ start,
+ end,
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt
new file mode 100644
index 000000000..1394c56ea
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Button.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import androidx.annotation.DrawableRes
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.Dp
+import androidx.wear.compose.material.Button
+import androidx.wear.compose.material.ButtonColors
+import androidx.wear.compose.material.ButtonDefaults
+import androidx.wear.compose.material.ButtonDefaults.DefaultButtonSize
+import androidx.wear.compose.material.ButtonDefaults.DefaultIconSize
+import androidx.wear.compose.material.ButtonDefaults.LargeButtonSize
+import androidx.wear.compose.material.ButtonDefaults.LargeIconSize
+import androidx.wear.compose.material.ButtonDefaults.SmallButtonSize
+import androidx.wear.compose.material.ButtonDefaults.SmallIconSize
+
+/**
+ * This component is an alternative to [Button], providing the following:
+ * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
+ * by the Wear guidelines;
+ */
+@Composable
+public fun Button(
+ imageVector: ImageVector,
+ contentDescription: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+ buttonSize: ButtonSize = ButtonSize.Default,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ enabled: Boolean = true
+) {
+ Button(
+ icon = imageVector,
+ contentDescription = contentDescription,
+ onClick = onClick,
+ modifier = modifier,
+ colors = colors,
+ buttonSize = buttonSize,
+ iconRtlMode = iconRtlMode,
+ enabled = enabled
+ )
+}
+
+/**
+ * This component is an alternative to [Button], providing the following:
+ * - a convenient way of providing an icon and choosing its size from a range of sizes recommended
+ * by the Wear guidelines;
+ */
+@Composable
+public fun Button(
+ @DrawableRes id: Int,
+ contentDescription: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+ buttonSize: ButtonSize = ButtonSize.Default,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ enabled: Boolean = true
+) {
+ Button(
+ icon = id,
+ contentDescription = contentDescription,
+ onClick = onClick,
+ modifier = modifier,
+ colors = colors,
+ buttonSize = buttonSize,
+ iconRtlMode = iconRtlMode,
+ enabled = enabled
+ )
+}
+
+@Composable
+internal fun Button(
+ icon: Any,
+ contentDescription: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ colors: ButtonColors = ButtonDefaults.primaryButtonColors(),
+ buttonSize: ButtonSize = ButtonSize.Default,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ enabled: Boolean = true
+) {
+ Button(
+ onClick = onClick,
+ modifier = modifier.size(buttonSize.tapTargetSize),
+ enabled = enabled,
+ colors = colors
+ ) {
+ val iconModifier = Modifier.size(buttonSize.iconSize).align(Alignment.Center)
+
+ Icon(
+ icon = icon,
+ contentDescription = contentDescription,
+ modifier = iconModifier,
+ rtlMode = iconRtlMode
+ )
+ }
+}
+
+public sealed class ButtonSize(public val iconSize: Dp, public val tapTargetSize: Dp) {
+ public object Default :
+ ButtonSize(iconSize = DefaultIconSize, tapTargetSize = DefaultButtonSize)
+
+ public object Large : ButtonSize(iconSize = LargeIconSize, tapTargetSize = LargeButtonSize)
+ public object Small : ButtonSize(iconSize = SmallIconSize, tapTargetSize = SmallButtonSize)
+
+ /**
+ * Custom sizes should follow the
+ * [accessibility principles and guidance for touch targets](https://developer.android.com/training/wearables/accessibility#set-minimum).
+ */
+ public data class Custom(val customIconSize: Dp, val customTapTargetSize: Dp) :
+ ButtonSize(iconSize = customIconSize, tapTargetSize = customTapTargetSize)
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt
new file mode 100644
index 000000000..ff3eddc65
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import android.graphics.drawable.Drawable
+import androidx.annotation.StringRes
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.Chip
+import androidx.wear.compose.material.ChipColors
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.ContentAlpha
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.contentColorFor
+
+/**
+ * This component is an alternative to [Chip], providing the following:
+ * - a convenient way of providing a label and a secondary label;
+ * - a convenient way of providing an icon, and choosing their size based on the sizes recommended
+ * by the Wear guidelines;
+ */
+@Composable
+public fun Chip(
+ label: String,
+ labelMaxLines: Int? = null,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLines: Int? = null,
+ icon: Any? = null,
+ iconContentDescription: String? = null,
+ largeIcon: Boolean = false,
+ textColor: Color = MaterialTheme.colors.onSurface,
+ iconColor: Color = Color.Unspecified,
+ colors: ChipColors = chipDefaultColors(),
+ enabled: Boolean = true
+) {
+ val iconParam: (@Composable BoxScope.() -> Unit)? =
+ icon?.let {
+ {
+ val iconSize =
+ if (largeIcon) {
+ ChipDefaults.LargeIconSize
+ } else {
+ ChipDefaults.IconSize
+ }
+
+ Row {
+ val iconModifier = Modifier.size(iconSize).clip(CircleShape)
+ when (icon) {
+ is ImageVector ->
+ Icon(
+ imageVector = icon,
+ tint = iconColor,
+ contentDescription = iconContentDescription,
+ modifier = iconModifier
+ )
+ is Int ->
+ Icon(
+ painter = painterResource(id = icon),
+ tint = iconColor,
+ contentDescription = iconContentDescription,
+ modifier = iconModifier
+ )
+ is Drawable ->
+ Icon(
+ painter = rememberDrawablePainter(icon),
+ tint = iconColor,
+ contentDescription = iconContentDescription,
+ modifier = iconModifier
+ )
+ else -> {}
+ }
+ }
+ }
+ }
+
+ Chip(
+ label = label,
+ labelMaxLines = labelMaxLines,
+ onClick = onClick,
+ modifier = modifier,
+ secondaryLabel = secondaryLabel,
+ secondaryLabelMaxLines = secondaryLabelMaxLines,
+ icon = iconParam,
+ largeIcon = largeIcon,
+ textColor = textColor,
+ colors = colors,
+ enabled = enabled
+ )
+}
+
+/**
+ * This component is an alternative to [Chip], providing the following:
+ * - a convenient way of providing a label and a secondary label;
+ * - a convenient way of providing an icon, and choosing their size based on the sizes recommended
+ * by the Wear guidelines;
+ */
+@Composable
+public fun Chip(
+ @StringRes labelId: Int,
+ labelMaxLines: Int? = null,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ @StringRes secondaryLabel: Int? = null,
+ secondaryLabelMaxLines: Int? = null,
+ icon: Any? = null,
+ largeIcon: Boolean = false,
+ textColor: Color = MaterialTheme.colors.onSurface,
+ iconColor: Color = Color.Unspecified,
+ colors: ChipColors = chipDefaultColors(),
+ enabled: Boolean = true
+) {
+ Chip(
+ label = stringResource(id = labelId),
+ labelMaxLines = labelMaxLines,
+ onClick = onClick,
+ modifier = modifier,
+ secondaryLabel = secondaryLabel?.let { stringResource(id = it) },
+ secondaryLabelMaxLines = secondaryLabelMaxLines,
+ icon = icon,
+ largeIcon = largeIcon,
+ textColor = textColor,
+ iconColor = iconColor,
+ colors = colors,
+ enabled = enabled
+ )
+}
+
+/**
+ * This component is an alternative to [Chip], providing the following:
+ * - a convenient way of providing a label and a secondary label;
+ */
+// Setting the color as per
+// https://source.corp.google.com/piper///depot/google3/java/com/google/android/clockwork/common/wearable/wearmaterial/button/res/color/wear_button_secondary_text_stateful.xml?q=wear_button_secondary_text_stateful
+@Composable
+public fun Chip(
+ label: String,
+ labelMaxLines: Int? = null,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLines: Int? = null,
+ icon: (@Composable BoxScope.() -> Unit)? = null,
+ largeIcon: Boolean = false,
+ textColor: Color = MaterialTheme.colors.onSurface,
+ secondaryTextColor: Color = MaterialTheme.colors.primary,
+ colors: ChipColors = chipDefaultColors(),
+ enabled: Boolean = true
+) {
+ val hasSecondaryLabel = secondaryLabel != null
+ val hasIcon = icon != null
+
+ val labelParam: (@Composable RowScope.() -> Unit) = {
+ Text(
+ text = label,
+ color = textColor,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = if (hasSecondaryLabel || hasIcon) TextAlign.Start else TextAlign.Center,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = labelMaxLines ?: if (hasSecondaryLabel) 1 else 2,
+ style = MaterialTheme.typography.button.copy(fontWeight = FontWeight.W600)
+ )
+ }
+
+ val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
+ secondaryLabel?.let {
+ {
+ Text(
+ text = secondaryLabel,
+ color = secondaryTextColor,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = secondaryLabelMaxLines ?: 1,
+ style = MaterialTheme.typography.caption2
+ )
+ }
+ }
+
+ val contentPadding =
+ if (largeIcon) {
+ val verticalPadding = ChipDefaults.ChipVerticalPadding
+ PaddingValues(
+ start = 10.dp,
+ top = verticalPadding,
+ end = ChipDefaults.ChipHorizontalPadding,
+ bottom = verticalPadding
+ )
+ } else {
+ ChipDefaults.ContentPadding
+ }
+
+ Chip(
+ label = labelParam,
+ onClick = onClick,
+ modifier = modifier.fillMaxWidth(),
+ secondaryLabel = secondaryLabelParam,
+ icon = icon,
+ colors = colors,
+ enabled = enabled,
+ contentPadding = contentPadding,
+ shape = RoundedCornerShape(26.dp)
+ )
+}
+
+/** Default colors of a Chip. */
+@Composable fun chipDefaultColors(): ChipColors = ChipDefaults.secondaryChipColors()
+
+/**
+ * ChipColors that disabled alpha is applied based on [ChipDefaults.secondaryChipColors()]. It is
+ * used for a Chip which would like to respond to click events, meanwhile it seems disabled.
+ */
+@Composable
+fun chipDisabledColors(): ChipColors {
+ val backgroundColor = MaterialTheme.colors.surface
+ val contentColor = contentColorFor(backgroundColor)
+ val secondaryContentColor = contentColor
+ val iconColor = contentColor
+
+ return ChipDefaults.chipColors(
+ backgroundColor = backgroundColor.copy(alpha = ContentAlpha.disabled),
+ contentColor = contentColor.copy(alpha = ContentAlpha.disabled),
+ secondaryContentColor = secondaryContentColor.copy(alpha = ContentAlpha.disabled),
+ iconColor = iconColor.copy(alpha = ContentAlpha.disabled)
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/DrawablePainter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/DrawablePainter.kt
new file mode 100644
index 000000000..d505a0ea4
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/DrawablePainter.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import android.view.View
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.RememberObserver
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.asAndroidColorFilter
+import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
+import androidx.compose.ui.graphics.nativeCanvas
+import androidx.compose.ui.graphics.painter.ColorPainter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.withSave
+import androidx.compose.ui.unit.LayoutDirection
+import kotlin.math.roundToInt
+
+private val MAIN_HANDLER by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
+
+/**
+ * A [Painter] which draws an Android [Drawable] and supports [Animatable] drawables. Instances
+ * should be remembered to be able to start and stop [Animatable] animations.
+ *
+ * Instances are usually retrieved from [rememberDrawablePainter].
+ */
+class DrawablePainter(val drawable: Drawable) : Painter(), RememberObserver {
+ private var drawInvalidateTick by mutableStateOf(0)
+ private var drawableIntrinsicSize by mutableStateOf(drawable.intrinsicSize)
+
+ private val callback: Drawable.Callback by lazy {
+ object : Drawable.Callback {
+ override fun invalidateDrawable(d: Drawable) {
+ // Update the tick so that we get re-drawn
+ drawInvalidateTick++
+ // Update our intrinsic size too
+ drawableIntrinsicSize = drawable.intrinsicSize
+ }
+
+ override fun scheduleDrawable(d: Drawable, what: Runnable, time: Long) {
+ MAIN_HANDLER.postAtTime(what, time)
+ }
+
+ override fun unscheduleDrawable(d: Drawable, what: Runnable) {
+ MAIN_HANDLER.removeCallbacks(what)
+ }
+ }
+ }
+
+ init {
+ if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) {
+ // Update the drawable's bounds to match the intrinsic size
+ drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
+ }
+ }
+
+ override fun onRemembered() {
+ drawable.callback = callback
+ drawable.setVisible(true, true)
+ if (drawable is Animatable) drawable.start()
+ }
+
+ override fun onAbandoned() = onForgotten()
+
+ override fun onForgotten() {
+ if (drawable is Animatable) drawable.stop()
+ drawable.setVisible(false, false)
+ drawable.callback = null
+ }
+
+ override fun applyAlpha(alpha: Float): Boolean {
+ drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255)
+ return true
+ }
+
+ override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
+ drawable.colorFilter = colorFilter?.asAndroidColorFilter()
+ return true
+ }
+
+ override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean {
+ if (Build.VERSION.SDK_INT >= 23) {
+ return drawable.setLayoutDirection(
+ when (layoutDirection) {
+ LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR
+ LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL
+ }
+ )
+ }
+ return false
+ }
+
+ override val intrinsicSize: Size
+ get() = drawableIntrinsicSize
+
+ override fun DrawScope.onDraw() {
+ drawIntoCanvas { canvas ->
+ // Reading this ensures that we invalidate when invalidateDrawable() is called
+ drawInvalidateTick
+
+ // Update the Drawable's bounds
+ drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt())
+
+ canvas.withSave { drawable.draw(canvas.nativeCanvas) }
+ }
+ }
+}
+
+/**
+ * Remembers [Drawable] wrapped up as a [Painter]. This function attempts to un-wrap the drawable
+ * contents and use Compose primitives where possible.
+ *
+ * If the provided [drawable] is `null`, an empty no-op painter is returned.
+ *
+ * This function tries to dispatch lifecycle events to [drawable] as much as possible from within
+ * Compose.
+ */
+@Composable
+fun rememberDrawablePainter(drawable: Drawable?): Painter =
+ remember(drawable) {
+ when (drawable) {
+ null -> EmptyPainter
+ is ColorDrawable -> ColorPainter(Color(drawable.color))
+ // Since the DrawablePainter will be remembered and it implements RememberObserver, it
+ // will receive the necessary events
+ else -> DrawablePainter(drawable.mutate())
+ }
+ }
+
+private val Drawable.intrinsicSize: Size
+ get() =
+ when {
+ // Only return a finite size if the drawable has an intrinsic size
+ intrinsicWidth >= 0 && intrinsicHeight >= 0 -> {
+ Size(width = intrinsicWidth.toFloat(), height = intrinsicHeight.toFloat())
+ }
+ else -> Size.Unspecified
+ }
+
+internal object EmptyPainter : Painter() {
+ override val intrinsicSize: Size
+ get() = Size.Unspecified
+ override fun DrawScope.onDraw() {}
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Icon.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Icon.kt
new file mode 100644
index 000000000..1a304b37e
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Icon.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import android.graphics.drawable.Drawable
+import androidx.annotation.DrawableRes
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.LocalContentAlpha
+import androidx.wear.compose.material.LocalContentColor
+
+/**
+ * This component is an alternative to [Icon], providing the following:
+ * - a convenient way of setting the icon to be mirrored in RTL mode;
+ */
+@Composable
+public fun Icon(
+ imageVector: ImageVector,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
+ rtlMode: IconRtlMode = IconRtlMode.Default
+) {
+ val shouldMirror =
+ rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl
+ Icon(
+ modifier = modifier.scale(scaleX = if (shouldMirror) -1f else 1f, scaleY = 1f),
+ imageVector = imageVector,
+ contentDescription = contentDescription,
+ tint = tint
+ )
+}
+
+/**
+ * This component is an alternative to [Icon], providing the following:
+ * - a convenient way of setting the icon to be mirrored in RTL mode;
+ */
+@Composable
+public fun Icon(
+ @DrawableRes id: Int,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
+ rtlMode: IconRtlMode = IconRtlMode.Default
+) {
+ val shouldMirror =
+ rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl
+
+ Icon(
+ painter = painterResource(id = id),
+ contentDescription = contentDescription,
+ modifier = modifier.scale(scaleX = if (shouldMirror) -1f else 1f, scaleY = 1f),
+ tint = tint
+ )
+}
+
+/**
+ * This component is an alternative to [Icon], providing the following:
+ * - a convenient way of providing an icon of various types
+ * - a convenient way of setting the icon to be mirrored in RTL mode;
+ */
+@Composable
+fun Icon(
+ icon: Any,
+ contentDescription: String?,
+ modifier: Modifier = Modifier,
+ tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
+ rtlMode: IconRtlMode = IconRtlMode.Default
+) {
+ val shouldMirror =
+ rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl
+
+ val iconModifier = modifier.scale(scaleX = if (shouldMirror) -1f else 1f, scaleY = 1f)
+ when (icon) {
+ is ImageVector -> {
+ Icon(
+ imageVector = icon,
+ modifier = iconModifier,
+ contentDescription = contentDescription,
+ tint = tint
+ )
+ }
+ is Int -> {
+ Icon(
+ painter = painterResource(id = icon),
+ contentDescription = contentDescription,
+ modifier = iconModifier,
+ tint = tint
+ )
+ }
+ is Drawable -> {
+ Icon(
+ painter = rememberDrawablePainter(icon),
+ contentDescription = contentDescription,
+ modifier = iconModifier,
+ tint = tint
+ )
+ }
+ else -> throw IllegalArgumentException("Type not supported.")
+ }
+}
+
+public enum class IconRtlMode {
+ Default,
+ Mirrored
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListFooter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListFooter.kt
new file mode 100644
index 000000000..5ed912ec6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListFooter.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.Icon
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+
+/** A slot based composable for creating a list footer item. */
+@Composable
+fun ListFooter(description: String, iconRes: Int? = null, onClick: (() -> Unit)? = null) {
+ val modifier = Modifier.fillMaxWidth()
+ Row(
+ modifier =
+ if (onClick == null) {
+ modifier
+ } else {
+ modifier.clickable(onClick = onClick)
+ }
+ ) {
+ iconRes?.let {
+ Spacer(modifier = Modifier.width(LeadingIconStartSpacing))
+ Icon(
+ painter = painterResource(id = it),
+ contentDescription = null,
+ modifier =
+ Modifier.size(LeadingIconSize, LeadingIconSize)
+ .align(Alignment.CenterVertically)
+ )
+ Spacer(modifier = Modifier.width(LeadingIconEndSpacing))
+ }
+ Text(
+ text = description,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Start,
+ overflow = TextOverflow.Ellipsis,
+ color = MaterialTheme.colors.onSurfaceVariant,
+ style = MaterialTheme.typography.caption2
+ )
+ }
+}
+
+/** The size of the spacing before the leading icon when they used inside a list footer. */
+private val LeadingIconStartSpacing = 4.dp
+
+/** The size of the spacing between the leading icon and a text inside a list footer. */
+private val LeadingIconEndSpacing = 8.dp
+
+/** The size of the leading icon. */
+private val LeadingIconSize = 24.dp
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt
new file mode 100644
index 000000000..259e1b0b2
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.heading
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.LocalContentColor
+import androidx.wear.compose.material.LocalTextStyle
+import androidx.wear.compose.material.MaterialTheme
+
+/**
+ * A slot based composable for creating a list header item. [ListHeader]s are typically expected to
+ * be a few words of text on a single line. The contents will be start and end padded.
+ *
+ * @param modifier The modifier for the [ListHeader].
+ * @param backgroundColor The background color to apply - typically Color.Transparent
+ * @param contentColor The color to apply to content.
+ * @param content Slot for [ListHeader] content, expected to be a single line of text.
+ */
+
+// Styling updated to match with wear material title
+// Ref:
+// https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:vendor/google_clockwork_partners/libs/ClockworkCommonLibs/common/wearable/wearmaterial/preference/res/layout/wear_title_preference.xml;l=1;drc=8ebd53cbba588e8e9aa964522fb05f4f5224609e;bpv=1;bpt=0
+@Composable
+fun ListHeader(
+ modifier: Modifier = Modifier,
+ backgroundColor: Color = Color.Transparent,
+ contentColor: Color = MaterialTheme.colors.onBackground,
+ content: @Composable RowScope.() -> Unit
+) {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier =
+ modifier.wrapContentSize().background(backgroundColor).semantics(
+ mergeDescendants = true
+ ) {
+ heading()
+ }
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides contentColor,
+ LocalTextStyle provides
+ MaterialTheme.typography.title3.copy(fontWeight = FontWeight.W600),
+ ) {
+ content()
+ }
+ }
+}
+
+/**
+ * A two slot based composable for creating a list subheader item. [ListSubheader]s offer slots for
+ * an icon and for a text label. The contents will be start and end padded.
+ *
+ * @param modifier The modifier for the [ListSubheader].
+ * @param backgroundColor The background color to apply - typically Color.Transparent
+ * @param contentColor The color to apply to content.
+ * @param icon A slot for providing icon to the [ListSubheader].
+ * @param label A slot for providing label to the [ListSubheader].
+ */
+@Composable
+fun ListSubheader(
+ modifier: Modifier = Modifier,
+ backgroundColor: Color = Color.Transparent,
+ contentColor: Color = MaterialTheme.colors.onBackground,
+ icon: (@Composable BoxScope.() -> Unit)? = null,
+ label: @Composable RowScope.() -> Unit,
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start,
+ modifier =
+ modifier
+ .height(IntrinsicSize.Min)
+ .fillMaxWidth()
+ .wrapContentSize(align = Alignment.CenterStart)
+ .background(backgroundColor)
+ .semantics(mergeDescendants = true) { heading() }
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides contentColor,
+ LocalTextStyle provides MaterialTheme.typography.caption1,
+ ) {
+ if (icon != null) {
+ Box(
+ modifier = Modifier.wrapContentSize(align = Alignment.CenterStart),
+ content = icon
+ )
+ Spacer(modifier = Modifier.width(6.dp))
+ }
+ label()
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ResponsiveDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ResponsiveDialog.kt
new file mode 100644
index 000000000..cf828b91d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ResponsiveDialog.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Arrangement.spacedBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.material.ButtonDefaults
+import androidx.wear.compose.material.ChipColors
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.LocalTextStyle
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumn
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults.responsive
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberColumnState
+
+// This file is a copy of ResponsiveDialogContent.kt from Horologist (go/horologist),
+// remove it once after wear compose supports large screen dialogs.
+
+@Composable
+fun ResponsiveDialogContent(
+ modifier: Modifier = Modifier,
+ icon: @Composable (() -> Unit)? = null,
+ title: @Composable (() -> Unit)? = null,
+ message: @Composable (() -> Unit)? = null,
+ onOk: (() -> Unit)? = null,
+ onCancel: (() -> Unit)? = null,
+ okButtonContentDescription: String = stringResource(android.R.string.ok),
+ cancelButtonContentDescription: String = stringResource(android.R.string.cancel),
+ state: ScalingLazyColumnState =
+ rememberColumnState(
+ responsive(
+ firstItemIsFullWidth = icon == null,
+ additionalPaddingAtBottom = 0.dp,
+ ),
+ ),
+ showPositionIndicator: Boolean = true,
+ content: (ScalingLazyListScope.() -> Unit)? = null,
+) {
+ Scaffold(
+ modifier = modifier.fillMaxSize(),
+ positionIndicator = {
+ if (showPositionIndicator) {
+ PositionIndicator(scalingLazyListState = state.state)
+ }
+ },
+ timeText = {},
+ ) {
+ // This will be applied only to the content.
+ CompositionLocalProvider(
+ LocalTextStyle provides MaterialTheme.typography.body2,
+ ) {
+ ScalingLazyColumn(columnState = state) {
+ icon?.let {
+ item {
+ Row(
+ Modifier.fillMaxWidth().padding(bottom = 4.dp), // 8.dp below icon
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ it()
+ }
+ }
+ }
+ title?.let {
+ item {
+ CompositionLocalProvider(
+ LocalTextStyle provides MaterialTheme.typography.title3,
+ ) {
+ Box(
+ Modifier.fillMaxWidth(titleMaxWidthFraction)
+ .padding(bottom = 8.dp), // 12.dp below icon
+ ) {
+ it()
+ }
+ }
+ }
+ }
+ if (icon == null && title == null) {
+ // Ensure the content is visible when there is nothing above it.
+ item { Spacer(Modifier.height(20.dp)) }
+ }
+ message?.let {
+ item {
+ Box(
+ Modifier.fillMaxWidth(messageMaxWidthFraction),
+ ) {
+ it()
+ }
+ }
+ }
+ content?.let { it() }
+ if (onOk != null || onCancel != null) {
+ item {
+ val width = LocalConfiguration.current.screenWidthDp
+ // Single buttons, or buttons on smaller screens are not meant to be
+ // responsive.
+ val buttonWidth =
+ if (width < 225 || onOk == null || onCancel == null) {
+ ButtonDefaults.DefaultButtonSize
+ } else {
+ // 14.56% on top of 5.2% margin on the sides, 12.dp between.
+ ((width * (1f - (2 * 0.1456f) - (2 * 0.052f)) - 12) / 2).dp
+ }
+ Row(
+ Modifier.fillMaxWidth()
+ .padding(
+ top = if (content != null || message != null) 12.dp else 0.dp,
+ ),
+ horizontalArrangement = spacedBy(12.dp, Alignment.CenterHorizontally),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ onCancel?.let {
+ ResponsiveButton(
+ icon = Icons.Default.Close,
+ cancelButtonContentDescription,
+ onClick = it,
+ buttonWidth,
+ ChipDefaults.secondaryChipColors(),
+ )
+ }
+ onOk?.let {
+ ResponsiveButton(
+ icon = Icons.Default.Check,
+ okButtonContentDescription,
+ onClick = it,
+ buttonWidth,
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ResponsiveButton(
+ icon: ImageVector,
+ contentDescription: String,
+ onClick: () -> Unit,
+ buttonWidth: Dp,
+ colors: ChipColors = ChipDefaults.primaryChipColors(),
+) {
+ androidx.wear.compose.material.Chip(
+ label = {
+ Box(Modifier.fillMaxWidth()) {
+ Icon(
+ imageVector = icon,
+ contentDescription = contentDescription,
+ modifier =
+ Modifier.size(ButtonDefaults.DefaultIconSize).align(Alignment.Center),
+ )
+ }
+ },
+ contentPadding = PaddingValues(0.dp),
+ shape = CircleShape,
+ onClick = onClick,
+ modifier = Modifier.width(buttonWidth),
+ colors = colors,
+ )
+}
+
+internal const val globalHorizontalPadding = 5.2f
+internal const val messageExtraHorizontalPadding = 4.56f
+internal const val titleExtraHorizontalPadding = 8.84f
+
+// Fraction of the max available width that message should take (after global and message padding)
+internal val messageMaxWidthFraction =
+ 1f -
+ 2f *
+ calculatePaddingFraction(
+ messageExtraHorizontalPadding,
+ )
+
+// Fraction of the max available width that title should take (after global and message padding)
+internal val titleMaxWidthFraction =
+ 1f -
+ 2f *
+ calculatePaddingFraction(
+ titleExtraHorizontalPadding,
+ )
+
+// Calculate total padding given global padding and additional padding required inside that.
+internal fun calculatePaddingFraction(extraPadding: Float) =
+ extraPadding / (100f - 2f * globalHorizontalPadding)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
new file mode 100644
index 000000000..717cf9528
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.graphics.drawable.Drawable
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.LocalLifecycleOwner
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.wear.compose.foundation.SwipeToDismissValue
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
+import androidx.wear.compose.material.CircularProgressIndicator
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.SwipeToDismissBox
+import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.material.VignettePosition
+import androidx.wear.compose.material.scrollAway
+import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rotaryWithScroll
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+
+/**
+ * Screen that contains a list of items defined using the [content] parameter, adds the time text
+ * (if [showTimeText] is true), the tile (if [title] is not null), the vignette and the position
+ * indicator. It also manages the scaling animation and allows the user to scroll the content using
+ * the crown.
+ */
+@Composable
+fun ScrollableScreen(
+ showTimeText: Boolean = true,
+ title: String? = null,
+ subtitle: CharSequence? = null,
+ image: Any? = null,
+ isLoading: Boolean = false,
+ titleTestTag: String? = null,
+ subtitleTestTag: String? = null,
+ content: ScalingLazyListScope.() -> Unit,
+) {
+ var dismissed by remember { mutableStateOf(false) }
+ val activity = LocalContext.current.findActivity()
+ val state = rememberSwipeToDismissBoxState()
+
+ LaunchedEffect(state.currentValue) {
+ if (state.currentValue == SwipeToDismissValue.Dismissed) {
+ dismiss(activity)
+ dismissed = true
+ state.snapTo(SwipeToDismissValue.Default)
+ }
+ }
+
+ // To support Swipe-dismiss effect,
+ // add the view to SwipeToDismissBox if the screen is not on the top fragment.
+ if (getBackStackEntryCount(activity) > 0) {
+ SwipeToDismissBox(state = state) { isBackground ->
+ Scaffold(
+ showTimeText,
+ title,
+ subtitle,
+ image,
+ isLoading = isLoading || isBackground || dismissed,
+ content,
+ titleTestTag,
+ subtitleTestTag
+ )
+ }
+ } else {
+ Scaffold(
+ showTimeText,
+ title,
+ subtitle,
+ image,
+ isLoading,
+ content,
+ titleTestTag,
+ subtitleTestTag
+ )
+ }
+}
+
+@Composable
+internal fun Scaffold(
+ showTimeText: Boolean,
+ title: String?,
+ subtitle: CharSequence?,
+ image: Any?,
+ isLoading: Boolean,
+ content: ScalingLazyListScope.() -> Unit,
+ titleTestTag: String? = null,
+ subtitleTestTag: String? = null,
+) {
+ val screenWidth = LocalConfiguration.current.screenWidthDp
+ val screenHeight = LocalConfiguration.current.screenHeightDp
+ val scrollContentHorizontalPadding = (screenWidth * 0.052).dp
+ val titleHorizontalPadding = (screenWidth * 0.0884).dp
+ val subtitleHorizontalPadding = (screenWidth * 0.0416).dp
+ val scrollContentTopPadding = (screenHeight * 0.1456).dp
+ val scrollContentBottomPadding = (screenHeight * 0.3636).dp
+ val titleBottomPadding =
+ if (subtitle == null) {
+ 8.dp
+ } else {
+ 4.dp
+ }
+ val subtitleBottomPadding = 8.dp
+ val timeTextTopPadding =
+ if (showTimeText) {
+ 1.dp
+ } else {
+ 0.dp
+ }
+ val titlePaddingValues =
+ PaddingValues(
+ start = titleHorizontalPadding,
+ top = 4.dp,
+ bottom = titleBottomPadding,
+ end = titleHorizontalPadding
+ )
+ val subTitlePaddingValues =
+ PaddingValues(
+ start = subtitleHorizontalPadding,
+ top = 4.dp,
+ bottom = subtitleBottomPadding,
+ end = subtitleHorizontalPadding
+ )
+ val initialCenterIndex = 0
+ val centerHeightDp = Dp(LocalConfiguration.current.screenHeightDp / 2.0f)
+ // We are adding TimeText's padding to create a smooth scrolling
+ val initialCenterItemScrollOffset = scrollContentTopPadding + timeTextTopPadding
+ val scrollAwayOffset = centerHeightDp - initialCenterItemScrollOffset
+ val focusRequester = remember { FocusRequester() }
+ val listState = remember { ScalingLazyListState(initialCenterItemIndex = initialCenterIndex) }
+ LaunchedEffect(title) {
+ listState.animateScrollToItem(index = 0) // Scroll to the top when triggerValue changes
+ }
+ WearPermissionTheme {
+ Scaffold(
+ // TODO: Use a rotary modifier from Wear Compose once Wear Compose 1.4 is landed.
+ // (b/325560444)
+ modifier =
+ Modifier.rotaryWithScroll(
+ scrollableState = listState,
+ focusRequester = focusRequester
+ ),
+ timeText = {
+ if (showTimeText && !isLoading) {
+ TimeText(
+ modifier =
+ Modifier.scrollAway(listState, initialCenterIndex, scrollAwayOffset)
+ .padding(top = timeTextTopPadding),
+ )
+ }
+ },
+ vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) },
+ positionIndicator = { PositionIndicator(scalingLazyListState = listState) }
+ ) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ if (isLoading) {
+ CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
+ } else {
+ ScalingLazyColumn(
+ modifier = Modifier.fillMaxWidth(),
+ state = listState,
+ // Set autoCentering to null to avoid adding extra padding based on the
+ // content.
+ autoCentering = null,
+ contentPadding =
+ PaddingValues(
+ start = scrollContentHorizontalPadding,
+ end = scrollContentHorizontalPadding,
+ top = scrollContentTopPadding,
+ bottom = scrollContentBottomPadding
+ )
+ ) {
+ image?.let {
+ val imageModifier = Modifier.size(24.dp)
+ when (image) {
+ is Int ->
+ item {
+ Image(
+ painter = painterResource(id = image),
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ modifier = imageModifier
+ )
+ }
+ is Drawable ->
+ item {
+ Image(
+ painter = rememberDrawablePainter(image),
+ contentDescription = null,
+ contentScale = ContentScale.Crop,
+ modifier = imageModifier
+ )
+ }
+ else -> {}
+ }
+ }
+ if (title != null) {
+ item {
+ var modifier: Modifier = Modifier
+ if (titleTestTag != null) {
+ modifier = modifier.testTag(titleTestTag)
+ }
+ ListHeader(modifier = Modifier.padding(titlePaddingValues)) {
+ Text(
+ text = title,
+ textAlign = TextAlign.Center,
+ modifier = modifier
+ )
+ }
+ }
+ }
+ if (subtitle != null) {
+ item {
+ var modifier: Modifier =
+ Modifier.align(Alignment.Center).padding(subTitlePaddingValues)
+ if (subtitleTestTag != null) {
+ modifier = modifier.testTag(subtitleTestTag)
+ }
+ AnnotatedText(
+ text = subtitle,
+ style =
+ MaterialTheme.typography.body2.copy(
+ color = MaterialTheme.colors.onSurfaceVariant
+ ),
+ modifier = modifier,
+ )
+ }
+ }
+
+ content()
+ }
+ RequestFocusOnResume(focusRequester = focusRequester)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun RequestFocusOnResume(focusRequester: FocusRequester) {
+ val lifecycleOwner = LocalLifecycleOwner.current
+ LaunchedEffect(Unit) {
+ lifecycleOwner.repeatOnLifecycle(state = Lifecycle.State.RESUMED) {
+ focusRequester.requestFocus()
+ }
+ }
+}
+
+internal fun dismiss(activity: Activity) {
+ if (activity is FragmentActivity) {
+ if (!activity.supportFragmentManager.popBackStackImmediate()) {
+ activity.finish()
+ }
+ } else {
+ activity.finish()
+ }
+}
+
+internal fun getBackStackEntryCount(activity: Activity): Int {
+ return if (activity is FragmentActivity) {
+ activity.supportFragmentManager.primaryNavigationFragment
+ ?.childFragmentManager
+ ?.backStackEntryCount
+ ?: 0
+ } else {
+ 0
+ }
+}
+
+internal fun Context.findActivity(): Activity {
+ var context = this
+ while (context is ContextWrapper) {
+ if (context is Activity) return context
+ context = context.baseContext
+ }
+ throw IllegalStateException("The screen should be called in the context of an Activity")
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt
new file mode 100644
index 000000000..fe1e75d67
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.ContentAlpha
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.ToggleChip
+import androidx.wear.compose.material.ToggleChipColors
+import androidx.wear.compose.material.ToggleChipDefaults
+import androidx.wear.compose.material.contentColorFor
+import com.android.permissioncontroller.R
+
+/**
+ * This component is an alternative to [ToggleChip], providing the following:
+ * - a convenient way of providing a label and a secondary label;
+ * - a convenient way of choosing the toggle control;
+ * - a convenient way of providing an icon and setting the icon to be mirrored in RTL mode;
+ */
+@Composable
+public fun ToggleChip(
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit,
+ label: String,
+ labelMaxLine: Int? = null,
+ toggleControl: ToggleChipToggleControl,
+ modifier: Modifier = Modifier,
+ icon: Any? = null,
+ iconColor: Color = Color.Unspecified,
+ iconRtlMode: IconRtlMode = IconRtlMode.Default,
+ secondaryLabel: String? = null,
+ secondaryLabelMaxLine: Int? = null,
+ colors: ToggleChipColors = ToggleChipDefaults.toggleChipColors(),
+ enabled: Boolean = true,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
+) {
+ val hasSecondaryLabel = secondaryLabel != null
+
+ val labelParam: (@Composable RowScope.() -> Unit) = {
+ Text(
+ text = label,
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Start,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = labelMaxLine ?: if (hasSecondaryLabel) 1 else 2,
+ style = MaterialTheme.typography.button
+ )
+ }
+
+ val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
+ secondaryLabel?.let {
+ {
+ Text(
+ text = secondaryLabel,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = secondaryLabelMaxLine ?: 1,
+ style = MaterialTheme.typography.caption2
+ )
+ }
+ }
+
+ val toggleControlParam: (@Composable () -> Unit) = {
+ Icon(
+ imageVector =
+ when (toggleControl) {
+ ToggleChipToggleControl.Switch -> ToggleChipDefaults.switchIcon(checked)
+ ToggleChipToggleControl.Radio -> ToggleChipDefaults.radioIcon(checked)
+ ToggleChipToggleControl.Checkbox -> ToggleChipDefaults.checkboxIcon(checked)
+ },
+ contentDescription = null,
+ // This potentially be removed once this issue is addressed:
+ // https://issuetracker.google.com/issues/287087138
+ rtlMode =
+ if (toggleControl == ToggleChipToggleControl.Switch) {
+ IconRtlMode.Mirrored
+ } else {
+ IconRtlMode.Default
+ }
+ )
+ }
+
+ val iconParam: (@Composable BoxScope.() -> Unit)? =
+ icon?.let {
+ {
+ Row {
+ Icon(
+ icon = icon,
+ tint = iconColor,
+ contentDescription = null,
+ modifier = Modifier.size(ChipDefaults.IconSize).clip(CircleShape),
+ rtlMode = iconRtlMode
+ )
+ }
+ }
+ }
+
+ val stateDescriptionSemantics =
+ stringResource(
+ if (checked) {
+ R.string.on
+ } else {
+ R.string.off
+ }
+ )
+ ToggleChip(
+ checked = checked,
+ onCheckedChange = onCheckedChanged,
+ label = labelParam,
+ toggleControl = toggleControlParam,
+ modifier =
+ modifier.fillMaxWidth().semantics { stateDescription = stateDescriptionSemantics },
+ appIcon = iconParam,
+ secondaryLabel = secondaryLabelParam,
+ colors = colors,
+ enabled = enabled,
+ interactionSource = interactionSource
+ )
+}
+
+/**
+ * ToggleChipColors that disabled alpha is applied based on [ToggleChipDefaults.toggleChipColors()].
+ * It is used for a ToggleChip which would like to respond to click events, meanwhile it seems
+ * disabled.
+ */
+@Composable
+fun toggleChipDisabledColors(): ToggleChipColors {
+ val checkedStartBackgroundColor =
+ MaterialTheme.colors.surface.copy(alpha = 0f).compositeOver(MaterialTheme.colors.surface)
+ val checkedEndBackgroundColor =
+ MaterialTheme.colors.primary.copy(alpha = 0.5f).compositeOver(MaterialTheme.colors.surface)
+ val checkedContentColor = MaterialTheme.colors.onSurface
+ val checkedSecondaryContentColor = MaterialTheme.colors.onSurfaceVariant
+ val checkedToggleControlColor = MaterialTheme.colors.secondary
+ val uncheckedStartBackgroundColor = MaterialTheme.colors.surface
+ val uncheckedEndBackgroundColor = uncheckedStartBackgroundColor
+ val uncheckedContentColor = contentColorFor(checkedEndBackgroundColor)
+ val uncheckedSecondaryContentColor = uncheckedContentColor
+ val uncheckedToggleControlColor = uncheckedContentColor
+
+ return ToggleChipDefaults.toggleChipColors(
+ checkedStartBackgroundColor =
+ checkedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
+ checkedEndBackgroundColor = checkedEndBackgroundColor.copy(alpha = ContentAlpha.disabled),
+ checkedContentColor = checkedContentColor.copy(alpha = ContentAlpha.disabled),
+ checkedSecondaryContentColor =
+ checkedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
+ checkedToggleControlColor = checkedToggleControlColor.copy(alpha = ContentAlpha.disabled),
+ uncheckedStartBackgroundColor =
+ uncheckedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
+ uncheckedEndBackgroundColor =
+ uncheckedEndBackgroundColor.copy(alpha = ContentAlpha.disabled),
+ uncheckedContentColor = uncheckedContentColor.copy(alpha = ContentAlpha.disabled),
+ uncheckedSecondaryContentColor =
+ uncheckedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
+ uncheckedToggleControlColor =
+ uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled)
+ )
+}
+
+/**
+ * ToggleChipColors that theme background color is applied based on
+ * [ToggleChipDefaults.toggleChipColors()]. It is used for a ToggleChip having the same background
+ * color of the screen.
+ */
+@Composable
+fun toggleChipBackgroundColors(): ToggleChipColors {
+ val checkedStartBackgroundColor =
+ MaterialTheme.colors.background
+ .copy(alpha = 0f)
+ .compositeOver(MaterialTheme.colors.background)
+ val checkedEndBackgroundColor =
+ MaterialTheme.colors.primary
+ .copy(alpha = 0.5f)
+ .compositeOver(MaterialTheme.colors.background)
+ val checkedContentColor = MaterialTheme.colors.onBackground
+ val checkedSecondaryContentColor = MaterialTheme.colors.onSurfaceVariant
+ val checkedToggleControlColor = MaterialTheme.colors.secondary
+ val uncheckedStartBackgroundColor = MaterialTheme.colors.background
+ val uncheckedEndBackgroundColor = uncheckedStartBackgroundColor
+ val uncheckedContentColor = contentColorFor(checkedEndBackgroundColor)
+ val uncheckedSecondaryContentColor = uncheckedContentColor
+ val uncheckedToggleControlColor = uncheckedContentColor
+
+ return ToggleChipDefaults.toggleChipColors(
+ checkedStartBackgroundColor = checkedStartBackgroundColor,
+ checkedEndBackgroundColor = checkedEndBackgroundColor,
+ checkedContentColor = checkedContentColor,
+ checkedSecondaryContentColor = checkedSecondaryContentColor,
+ checkedToggleControlColor = checkedToggleControlColor,
+ uncheckedStartBackgroundColor = uncheckedStartBackgroundColor,
+ uncheckedEndBackgroundColor = uncheckedEndBackgroundColor,
+ uncheckedContentColor = uncheckedContentColor,
+ uncheckedSecondaryContentColor = uncheckedSecondaryContentColor,
+ uncheckedToggleControlColor = uncheckedToggleControlColor
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt
new file mode 100644
index 000000000..a4ce4e764
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements
+
+public enum class ToggleChipToggleControl {
+ Switch,
+ Radio,
+ Checkbox
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnDefaults.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnDefaults.kt
new file mode 100644
index 000000000..550f1dc24
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnDefaults.kt
@@ -0,0 +1,245 @@
+/*
+ * Copyright 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("ObjectLiteralToLambda")
+
+package com.android.permissioncontroller.permission.ui.wear.elements.layout
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.times
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import androidx.wear.compose.foundation.lazy.ScalingParams
+import androidx.wear.compose.material.ChipDefaults
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState.RotaryMode
+import kotlin.math.sqrt
+
+// This file's content is copied from ScalingLazyColumnDefaults.kt from Horologist (go/horologist),
+// remove it once after wear compose supports large screen dialogs.
+
+/** Default layouts for ScalingLazyColumnState, based on UX guidance. */
+object ScalingLazyColumnDefaults {
+
+ /**
+ * Creates a Responsive layout for ScalingLazyColumn. The first and last items will scroll just
+ * onto screen at full size, assuming rounded corners of a Chip.
+ *
+ * @param firstItemIsFullWidth set to false if the first item is small enough to fit at the top,
+ * however it may be scaled.
+ * @param additionalPaddingAtBottom additional padding at end of content to avoid problem items
+ * clipping
+ * @param verticalArrangement the ScalingLazyColumn verticalArrangement.
+ * @param horizontalPaddingPercent the amount of horizontal padding as a percent.
+ * @param rotaryMode the rotary handling, such as Fling or Snap.
+ * @param hapticsEnabled whether haptics are enabled.
+ * @param reverseLayout whether to start at the bottom.
+ * @param userScrollEnabled whether to allow user to scroll.
+ */
+ // @Deprecated("Replaced by rememberResponsiveColumnState")
+
+ fun responsive(
+ firstItemIsFullWidth: Boolean = true,
+ additionalPaddingAtBottom: Dp = 10.dp,
+ verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = Alignment.Top,
+ ),
+ horizontalPaddingPercent: Float = 0.052f,
+ rotaryMode: RotaryMode? = RotaryMode.Scroll,
+ hapticsEnabled: Boolean = true,
+ reverseLayout: Boolean = false,
+ userScrollEnabled: Boolean = true,
+ ): ScalingLazyColumnState.Factory {
+ return object : ScalingLazyColumnState.Factory {
+ @Composable
+ override fun create(): ScalingLazyColumnState {
+ val density = LocalDensity.current
+ val configuration = LocalConfiguration.current
+ val screenWidthDp = configuration.screenWidthDp.toFloat()
+ val screenHeightDp = configuration.screenHeightDp.toFloat()
+
+ return remember {
+ val padding = screenWidthDp * horizontalPaddingPercent
+ val topPaddingDp: Dp =
+ if (firstItemIsFullWidth && configuration.isScreenRound) {
+ calculateVerticalOffsetForChip(screenWidthDp, horizontalPaddingPercent)
+ } else {
+ 32.dp
+ }
+ val bottomPaddingDp: Dp =
+ if (configuration.isScreenRound) {
+ calculateVerticalOffsetForChip(
+ screenWidthDp,
+ horizontalPaddingPercent,
+ ) + additionalPaddingAtBottom
+ } else {
+ 0.dp
+ }
+ val contentPadding =
+ PaddingValues(
+ start = padding.dp,
+ end = padding.dp,
+ top = topPaddingDp,
+ bottom = bottomPaddingDp,
+ )
+
+ val scalingParams = responsiveScalingParams(screenWidthDp)
+
+ val screenHeightPx = with(density) { screenHeightDp.dp.roundToPx() }
+ val topPaddingPx = with(density) { topPaddingDp.roundToPx() }
+ val topScreenOffsetPx = screenHeightPx / 2 - topPaddingPx
+
+ val initialScrollPosition =
+ ScalingLazyColumnState.ScrollPosition(
+ index = 0,
+ offsetPx = topScreenOffsetPx,
+ )
+ ScalingLazyColumnState(
+ initialScrollPosition = initialScrollPosition,
+ autoCentering = null,
+ anchorType = ScalingLazyListAnchorType.ItemStart,
+ rotaryMode = rotaryMode,
+ verticalArrangement = verticalArrangement,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ contentPadding = contentPadding,
+ scalingParams = scalingParams,
+ hapticsEnabled = hapticsEnabled,
+ reverseLayout = reverseLayout,
+ userScrollEnabled = userScrollEnabled,
+ )
+ }
+ }
+ }
+ }
+
+ internal fun calculateVerticalOffsetForChip(
+ viewportDiameter: Float,
+ horizontalPaddingPercent: Float,
+ ): Dp {
+ val childViewHeight: Float = ChipDefaults.Height.value
+ val childViewWidth: Float = viewportDiameter * (1.0f - (2f * horizontalPaddingPercent))
+ val radius = viewportDiameter / 2f
+ return (radius -
+ sqrt(
+ (radius - childViewHeight + childViewWidth * 0.5f) *
+ (radius - childViewWidth * 0.5f),
+ ) -
+ childViewHeight * 0.5f)
+ .dp
+ }
+
+ fun responsiveScalingParams(screenWidthDp: Float): ScalingParams {
+ val sizeRatio = ((screenWidthDp - 192) / (233 - 192).toFloat()).coerceIn(0f, 1.5f)
+ val presetRatio = 0f
+
+ val minElementHeight = lerp(0.2f, 0.157f, sizeRatio)
+ val maxElementHeight = lerp(0.6f, 0.472f, sizeRatio).coerceAtLeast(minElementHeight)
+ val minTransitionArea = lerp(0.35f, lerp(0.35f, 0.393f, presetRatio), sizeRatio)
+ val maxTransitionArea = lerp(0.55f, lerp(0.55f, 0.593f, presetRatio), sizeRatio)
+
+ val scalingParams =
+ ScalingLazyColumnDefaults.scalingParams(
+ minElementHeight = minElementHeight,
+ maxElementHeight = maxElementHeight,
+ minTransitionArea = minTransitionArea,
+ maxTransitionArea = maxTransitionArea,
+ )
+ return scalingParams
+ }
+
+ internal val Padding12Pct = 0.1248f
+ internal val Padding16Pct = 0.1664f
+ internal val Padding20Pct = 0.2083f
+ internal val Padding21Pct = 0.2188f
+ internal val Padding31Pct = 0.3646f
+
+ enum class ItemType(
+ val topPaddingDp: Float,
+ val bottomPaddingDp: Float,
+ val paddingCorrection: Dp = 0.dp,
+ ) {
+ Card(Padding21Pct, Padding31Pct),
+ Chip(Padding21Pct, Padding31Pct),
+ CompactChip(
+ topPaddingDp = Padding12Pct,
+ bottomPaddingDp = Padding20Pct,
+ paddingCorrection = (-8).dp,
+ ),
+ Icon(Padding12Pct, Padding21Pct),
+ MultiButton(Padding21Pct, Padding20Pct),
+ SingleButton(Padding12Pct, Padding20Pct),
+ Text(Padding21Pct, Padding31Pct),
+ Unspecified(0f, 0f),
+ }
+
+ @Composable
+ fun padding(
+ first: ItemType = ItemType.Unspecified,
+ last: ItemType = ItemType.Unspecified,
+ horizontalPercent: Float = 0.052f,
+ ): @Composable () -> PaddingValues {
+ val configuration = LocalConfiguration.current
+ val screenWidthDp = configuration.screenWidthDp.toFloat()
+ val screenHeightDp = configuration.screenHeightDp.toFloat()
+
+ return {
+ val height = screenHeightDp.dp
+ val horizontalPadding = screenWidthDp.dp * horizontalPercent
+
+ val topPadding =
+ if (first != ItemType.Unspecified) {
+ first.topPaddingDp * height + first.paddingCorrection
+ } else {
+ if (configuration.isScreenRound) {
+ calculateVerticalOffsetForChip(screenWidthDp, horizontalPercent)
+ } else {
+ 32.dp
+ }
+ }
+
+ val bottomPadding =
+ if (last != ItemType.Unspecified) {
+ last.bottomPaddingDp * height + first.paddingCorrection
+ } else {
+ if (configuration.isScreenRound) {
+ calculateVerticalOffsetForChip(
+ screenWidthDp,
+ horizontalPercent,
+ ) + 10.dp
+ } else {
+ 0.dp
+ }
+ }
+
+ PaddingValues(
+ top = topPadding,
+ bottom = bottomPadding,
+ start = horizontalPadding,
+ end = horizontalPadding,
+ )
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnState.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnState.kt
new file mode 100644
index 000000000..0603647b1
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnState.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("ObjectLiteralToLambda")
+@file:OptIn(ExperimentalWearFoundationApi::class)
+
+package com.android.permissioncontroller.permission.ui.wear.elements.layout
+
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults as WearScalingLazyColumnDefaults
+import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType
+import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.ScalingParams
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults.responsiveScalingParams
+import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState.RotaryMode
+import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rememberDisabledHaptic
+import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rememberRotaryHapticHandler
+import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rotaryWithScroll
+import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.rotaryWithSnap
+import com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput.toRotaryScrollAdapter
+
+// This file is a copy of ScalingLazyColumnState.kt from Horologist (go/horologist),
+// remove it once after wear compose supports large screen dialogs.
+
+/**
+ * A Config and State object wrapping up all configuration for a [ScalingLazyColumn]. This allows
+ * defaults such as [ScalingLazyColumnDefaults.responsive].
+ */
+class ScalingLazyColumnState(
+ val initialScrollPosition: ScrollPosition = ScrollPosition(1, 0),
+ val autoCentering: AutoCenteringParams? =
+ AutoCenteringParams(
+ initialScrollPosition.index,
+ initialScrollPosition.offsetPx,
+ ),
+ val anchorType: ScalingLazyListAnchorType = ScalingLazyListAnchorType.ItemCenter,
+ val contentPadding: PaddingValues = PaddingValues(horizontal = 10.dp),
+ val rotaryMode: RotaryMode? = RotaryMode.Scroll,
+ val reverseLayout: Boolean = false,
+ val verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = if (!reverseLayout) Alignment.Top else Alignment.Bottom,
+ ),
+ val horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ val flingBehavior: FlingBehavior? = null,
+ val userScrollEnabled: Boolean = true,
+ val scalingParams: ScalingParams = WearScalingLazyColumnDefaults.scalingParams(),
+ val hapticsEnabled: Boolean = true,
+) : ScrollableState {
+ private var _state: ScalingLazyListState? = null
+ var state: ScalingLazyListState
+ get() {
+ if (_state == null) {
+ _state =
+ ScalingLazyListState(
+ initialScrollPosition.index,
+ initialScrollPosition.offsetPx,
+ )
+ }
+ return _state!!
+ }
+ set(value) {
+ _state = value
+ }
+
+ override val canScrollBackward: Boolean
+ get() = state.canScrollBackward
+
+ override val canScrollForward: Boolean
+ get() = state.canScrollForward
+
+ override val isScrollInProgress: Boolean
+ get() = state.isScrollInProgress
+
+ override fun dispatchRawDelta(delta: Float): Float = state.dispatchRawDelta(delta)
+
+ override suspend fun scroll(
+ scrollPriority: MutatePriority,
+ block: suspend ScrollScope.() -> Unit,
+ ) {
+ state.scroll(scrollPriority, block)
+ }
+
+ sealed interface RotaryMode {
+ data object Snap : RotaryMode
+
+ data object Scroll : RotaryMode
+ }
+
+ data class ScrollPosition(
+ val index: Int,
+ val offsetPx: Int,
+ )
+
+ fun interface Factory {
+ @Composable fun create(): ScalingLazyColumnState
+ }
+}
+
+// @Deprecated("Replaced by rememberResponsiveColumnState")
+@Composable
+fun rememberColumnState(
+ factory: ScalingLazyColumnState.Factory = ScalingLazyColumnDefaults.responsive(),
+): ScalingLazyColumnState {
+ val columnState = factory.create()
+
+ columnState.state = rememberSaveable(saver = ScalingLazyListState.Saver) { columnState.state }
+
+ return columnState
+}
+
+@Composable
+fun rememberResponsiveColumnState(
+ contentPadding: @Composable () -> PaddingValues =
+ ScalingLazyColumnDefaults.padding(
+ first = ScalingLazyColumnDefaults.ItemType.Unspecified,
+ last = ScalingLazyColumnDefaults.ItemType.Unspecified,
+ ),
+ verticalArrangement: Arrangement.Vertical =
+ Arrangement.spacedBy(
+ space = 4.dp,
+ alignment = Alignment.Top,
+ ),
+ rotaryMode: RotaryMode? = RotaryMode.Scroll,
+ hapticsEnabled: Boolean = true,
+ reverseLayout: Boolean = false,
+ userScrollEnabled: Boolean = true,
+): ScalingLazyColumnState {
+ val density = LocalDensity.current
+ val configuration = LocalConfiguration.current
+ val screenWidthDp = configuration.screenWidthDp.toFloat()
+ val screenHeightDp = configuration.screenHeightDp.toFloat()
+
+ val scalingParams = responsiveScalingParams(screenWidthDp)
+
+ val contentPaddingCalculated = contentPadding()
+
+ val screenHeightPx = with(density) { screenHeightDp.dp.roundToPx() }
+ val topPaddingPx = with(density) { contentPaddingCalculated.calculateTopPadding().roundToPx() }
+ val topScreenOffsetPx = screenHeightPx / 2 - topPaddingPx
+
+ val initialScrollPosition =
+ ScalingLazyColumnState.ScrollPosition(
+ index = 0,
+ offsetPx = topScreenOffsetPx,
+ )
+
+ val columnState =
+ ScalingLazyColumnState(
+ initialScrollPosition = initialScrollPosition,
+ autoCentering = null,
+ anchorType = ScalingLazyListAnchorType.ItemStart,
+ rotaryMode = rotaryMode,
+ verticalArrangement = verticalArrangement,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ contentPadding = contentPaddingCalculated,
+ scalingParams = scalingParams,
+ hapticsEnabled = hapticsEnabled,
+ reverseLayout = reverseLayout,
+ userScrollEnabled = userScrollEnabled,
+ )
+
+ columnState.state = rememberSaveable(saver = ScalingLazyListState.Saver) { columnState.state }
+
+ return columnState
+}
+
+@Composable
+fun ScalingLazyColumn(
+ columnState: ScalingLazyColumnState,
+ modifier: Modifier = Modifier,
+ content: ScalingLazyListScope.() -> Unit,
+) {
+ val focusRequester = rememberActiveFocusRequester()
+
+ val rotaryHaptics =
+ if (columnState.hapticsEnabled) {
+ rememberRotaryHapticHandler(columnState.state)
+ } else {
+ rememberDisabledHaptic()
+ }
+
+ val modifierWithRotary =
+ when (columnState.rotaryMode) {
+ RotaryMode.Snap ->
+ modifier.rotaryWithSnap(
+ focusRequester = focusRequester,
+ rotaryScrollAdapter = columnState.state.toRotaryScrollAdapter(),
+ reverseDirection = columnState.reverseLayout,
+ rotaryHaptics = rotaryHaptics,
+ )
+ RotaryMode.Scroll ->
+ modifier.rotaryWithScroll(
+ focusRequester = focusRequester,
+ scrollableState = columnState.state,
+ reverseDirection = columnState.reverseLayout,
+ rotaryHaptics = rotaryHaptics,
+ )
+ else -> modifier
+ }
+
+ ScalingLazyColumn(
+ modifier = modifierWithRotary.fillMaxSize(),
+ state = columnState.state,
+ contentPadding = columnState.contentPadding,
+ reverseLayout = columnState.reverseLayout,
+ verticalArrangement = columnState.verticalArrangement,
+ horizontalAlignment = columnState.horizontalAlignment,
+ flingBehavior = columnState.flingBehavior ?: ScrollableDefaults.flingBehavior(),
+ userScrollEnabled = columnState.userScrollEnabled,
+ scalingParams = columnState.scalingParams,
+ anchorType = columnState.anchorType,
+ autoCentering = columnState.autoCentering,
+ content = content,
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Haptics.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Haptics.kt
new file mode 100644
index 000000000..817bf7efe
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Haptics.kt
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput
+
+import android.os.Build
+import android.view.View
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalView
+import kotlin.math.abs
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.withContext
+
+// This file is a copy of Haptics.kt from Horologist (go/horologist),
+// remove it once Wear Compose 1.4 is landed (b/325560444).
+
+private const val DEBUG = false
+
+/** Debug logging that can be enabled. */
+private inline fun debugLog(generateMsg: () -> String) {
+ if (DEBUG) {
+ println("RotaryHaptics: ${generateMsg()}")
+ }
+}
+
+/**
+ * Throttling events within specified timeframe. Only first and last events will be received. For a
+ * flow emitting elements 1 to 30, with a 100ms delay between them:
+ * ```
+ * val flow = flow {
+ * for (i in 1..30) {
+ * delay(100)
+ * emit(i)
+ * }
+ * }
+ * ```
+ *
+ * With timeframe=1000 only those integers will be received: 1, 10, 20, 30 .
+ */
+internal fun <T> Flow<T>.throttleLatest(timeframe: Long): Flow<T> = flow {
+ conflate().collect {
+ emit(it)
+ delay(timeframe)
+ }
+}
+
+/** Handles haptics for rotary usage */
+interface RotaryHapticHandler {
+
+ /** Handles haptics when scroll is used */
+ fun handleScrollHaptic(scrollDelta: Float)
+
+ /** Handles haptics when scroll with snap is used */
+ fun handleSnapHaptic(scrollDelta: Float)
+}
+
+/**
+ * Default implementation of [RotaryHapticHandler]. It handles haptic feedback based on the
+ * [scrollableState], scrolled pixels and [hapticsThresholdPx]. Haptic is not fired in this class,
+ * instead it's sent to [hapticsChannel] where it'll performed later.
+ *
+ * @param scrollableState Haptic performed based on this state
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ */
+class DefaultRotaryHapticHandler(
+ private val scrollableState: ScrollableState,
+ private val hapticsChannel: Channel<RotaryHapticsType>,
+ private val hapticsThresholdPx: Long = 50,
+) : RotaryHapticHandler {
+
+ private var overscrollHapticTriggered = false
+ private var currScrollPosition = 0f
+ private var prevHapticsPosition = 0f
+
+ override fun handleScrollHaptic(scrollDelta: Float) {
+ if (
+ (scrollDelta > 0 && !scrollableState.canScrollForward) ||
+ (scrollDelta < 0 && !scrollableState.canScrollBackward)
+ ) {
+ if (!overscrollHapticTriggered) {
+ trySendHaptic(RotaryHapticsType.ScrollLimit)
+ overscrollHapticTriggered = true
+ }
+ } else {
+ overscrollHapticTriggered = false
+ currScrollPosition += scrollDelta
+ val diff = abs(currScrollPosition - prevHapticsPosition)
+
+ if (diff >= hapticsThresholdPx) {
+ trySendHaptic(RotaryHapticsType.ScrollTick)
+ prevHapticsPosition = currScrollPosition
+ }
+ }
+ }
+
+ override fun handleSnapHaptic(scrollDelta: Float) {
+ if (
+ (scrollDelta > 0 && !scrollableState.canScrollForward) ||
+ (scrollDelta < 0 && !scrollableState.canScrollBackward)
+ ) {
+ if (!overscrollHapticTriggered) {
+ trySendHaptic(RotaryHapticsType.ScrollLimit)
+ overscrollHapticTriggered = true
+ }
+ } else {
+ overscrollHapticTriggered = false
+ trySendHaptic(RotaryHapticsType.ScrollItemFocus)
+ }
+ }
+
+ private fun trySendHaptic(rotaryHapticsType: RotaryHapticsType) {
+ // Ok to ignore the ChannelResult because we default to capacity = 2 and DROP_OLDEST
+ @Suppress("UNUSED_VARIABLE") val unused = hapticsChannel.trySend(rotaryHapticsType)
+ }
+}
+
+/** Interface for Rotary haptic feedback */
+interface RotaryHapticFeedback {
+ fun performHapticFeedback(type: RotaryHapticsType)
+}
+
+/** Rotary haptic types */
+@JvmInline
+value class RotaryHapticsType(private val type: Int) {
+ companion object {
+ /**
+ * A scroll ticking haptic. Similar to texture haptic - performed each time when a
+ * scrollable content is scrolled by a certain distance
+ */
+ val ScrollTick: RotaryHapticsType = RotaryHapticsType(1)
+
+ /**
+ * An item focus (snap) haptic. Performed when a scrollable content is snapped to a specific
+ * item.
+ */
+ val ScrollItemFocus: RotaryHapticsType = RotaryHapticsType(2)
+
+ /**
+ * A limit(overscroll) haptic. Performed when a list reaches the limit (start or end) and
+ * can't scroll further
+ */
+ val ScrollLimit: RotaryHapticsType = RotaryHapticsType(3)
+ }
+}
+
+/** Remember disabled haptics handler */
+@Composable
+fun rememberDisabledHaptic(): RotaryHapticHandler = remember {
+ object : RotaryHapticHandler {
+
+ override fun handleScrollHaptic(scrollDelta: Float) {
+ // Do nothing
+ }
+
+ override fun handleSnapHaptic(scrollDelta: Float) {
+ // Do nothing
+ }
+ }
+}
+
+/**
+ * Remember rotary haptic handler.
+ *
+ * @param scrollableState A scrollableState, used to determine whether the end of the scrollable was
+ * reached or not.
+ * @param throttleThresholdMs Throttling events within specified timeframe. Only first and last
+ * events will be received. Check [throttleLatest] for more info.
+ * @param hapticsThresholdPx A scroll threshold after which haptic is produced.
+ * @param hapticsChannel Channel to which haptic events will be sent
+ * @param rotaryHaptics Interface for Rotary haptic feedback which performs haptics
+ */
+@Composable
+fun rememberRotaryHapticHandler(
+ scrollableState: ScrollableState,
+ throttleThresholdMs: Long = 30,
+ hapticsThresholdPx: Long = 50,
+ hapticsChannel: Channel<RotaryHapticsType> = rememberHapticChannel(),
+ rotaryHaptics: RotaryHapticFeedback = rememberDefaultRotaryHapticFeedback(),
+): RotaryHapticHandler {
+ return remember(scrollableState, hapticsChannel, rotaryHaptics) {
+ DefaultRotaryHapticHandler(scrollableState, hapticsChannel, hapticsThresholdPx)
+ }
+ .apply {
+ LaunchedEffect(hapticsChannel) {
+ hapticsChannel.receiveAsFlow().throttleLatest(throttleThresholdMs).collect {
+ hapticType ->
+ // 'withContext' launches performHapticFeedback in a separate thread,
+ // as otherwise it produces a visible lag (b/219776664)
+ val currentTime = System.currentTimeMillis()
+ debugLog { "Haptics started" }
+ withContext(Dispatchers.Default) {
+ debugLog {
+ "Performing haptics, delay: " +
+ "${System.currentTimeMillis() - currentTime}"
+ }
+ rotaryHaptics.performHapticFeedback(hapticType)
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun rememberHapticChannel() = remember {
+ Channel<RotaryHapticsType>(
+ capacity = 2,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
+ )
+}
+
+@Composable
+public fun rememberDefaultRotaryHapticFeedback(): RotaryHapticFeedback =
+ LocalView.current.let { view -> remember { findDeviceSpecificHapticFeedback(view) } }
+
+internal fun findDeviceSpecificHapticFeedback(view: View): RotaryHapticFeedback =
+ if (isSamsungWatch()) {
+ SamsungWatchHapticFeedback(view)
+ } else {
+ DefaultRotaryHapticFeedback(view)
+ }
+
+/** Default Rotary implementation for [RotaryHapticFeedback] */
+class DefaultRotaryHapticFeedback(private val view: View) : RotaryHapticFeedback {
+
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ view.performHapticFeedback(SCROLL_ITEM_FOCUS)
+ }
+ RotaryHapticsType.ScrollTick -> {
+ view.performHapticFeedback(SCROLL_TICK)
+ }
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(SCROLL_LIMIT)
+ }
+ }
+ }
+
+ private companion object {
+ // Hidden constants from HapticFeedbackConstants
+ const val SCROLL_TICK: Int = 18
+ const val SCROLL_ITEM_FOCUS: Int = 19
+ const val SCROLL_LIMIT: Int = 20
+ }
+}
+
+/** Implementation of [RotaryHapticFeedback] for Samsung devices */
+private class SamsungWatchHapticFeedback(private val view: View) : RotaryHapticFeedback {
+ override fun performHapticFeedback(
+ type: RotaryHapticsType,
+ ) {
+ when (type) {
+ RotaryHapticsType.ScrollItemFocus -> {
+ view.performHapticFeedback(102)
+ }
+ RotaryHapticsType.ScrollTick -> {
+ view.performHapticFeedback(102)
+ }
+ RotaryHapticsType.ScrollLimit -> {
+ view.performHapticFeedback(50107)
+ }
+ }
+ }
+}
+
+private fun isSamsungWatch(): Boolean = Build.MANUFACTURER.contains("Samsung", ignoreCase = true)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Rotary.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Rotary.kt
new file mode 100644
index 000000000..19a6ea671
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/Rotary.kt
@@ -0,0 +1,1232 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput
+
+import android.view.ViewConfiguration
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.SpringSpec
+import androidx.compose.animation.core.animateTo
+import androidx.compose.animation.core.copy
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollableDefaults
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.rotary.RotaryInputModifierNode
+import androidx.compose.ui.input.rotary.RotaryScrollEvent
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.util.fastSumBy
+import androidx.compose.ui.util.lerp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.rememberActiveFocusRequester
+import kotlin.math.abs
+import kotlin.math.absoluteValue
+import kotlin.math.sign
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.transformLatest
+import kotlinx.coroutines.launch
+
+// This file is a copy of Rotary.kt from Horologist (go/horologist),
+// remove it once Wear Compose 1.4 is landed (b/325560444).
+
+/**
+ * A modifier which connects rotary events with scrollable. This modifier supports scroll with
+ * fling.
+ *
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param focusRequester Requests the focus for rotary input. By default comes from
+ * [rememberActiveFocusRequester], which is used with [HierarchicalFocusCoordinator]
+ * @param flingBehavior Logic describing fling behavior. If null fling will not happen.
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with Scrollable
+ * `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@Suppress("ComposableModifierFactory")
+@Composable
+fun Modifier.rotaryWithScroll(
+ scrollableState: ScrollableState,
+ focusRequester: FocusRequester = rememberActiveFocusRequester(),
+ flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
+ rotaryHaptics: RotaryHapticHandler = rememberRotaryHapticHandler(scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier =
+ rotaryHandler(
+ rotaryScrollHandler =
+ RotaryDefaults.rememberFlingHandler(scrollableState, flingBehavior),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "rotaryWithFling"
+ properties["scrollableState"] = scrollableState
+ properties["focusRequester"] = focusRequester
+ properties["flingBehavior"] = flingBehavior
+ properties["rotaryHaptics"] = rotaryHaptics
+ properties["reverseDirection"] = reverseDirection
+ },
+ )
+ .focusRequester(focusRequester)
+ .focusable()
+
+/**
+ * A modifier which connects rotary events with scrollable. This modifier supports snap.
+ *
+ * @param focusRequester Requests the focus for rotary input. By default comes from
+ * [rememberActiveFocusRequester], which is used with [HierarchicalFocusCoordinator]
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param rotaryHaptics Class which will handle haptic feedback
+ * @param reverseDirection Reverse the direction of scrolling. Should be aligned with Scrollable
+ * `reverseDirection` parameter
+ */
+@OptIn(ExperimentalWearFoundationApi::class)
+@Suppress("ComposableModifierFactory")
+@Composable
+fun Modifier.rotaryWithSnap(
+ rotaryScrollAdapter: RotaryScrollAdapter,
+ focusRequester: FocusRequester = rememberActiveFocusRequester(),
+ snapParameters: SnapParameters = RotaryDefaults.snapParametersDefault,
+ rotaryHaptics: RotaryHapticHandler =
+ rememberRotaryHapticHandler(rotaryScrollAdapter.scrollableState),
+ reverseDirection: Boolean = false,
+): Modifier =
+ rotaryHandler(
+ rotaryScrollHandler =
+ RotaryDefaults.rememberSnapHandler(rotaryScrollAdapter, snapParameters),
+ reverseDirection = reverseDirection,
+ rotaryHaptics = rotaryHaptics,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "rotaryWithFling"
+ properties["rotaryScrollAdapter"] = rotaryScrollAdapter
+ properties["focusRequester"] = focusRequester
+ properties["snapParameters"] = snapParameters
+ properties["rotaryHaptics"] = rotaryHaptics
+ properties["reverseDirection"] = reverseDirection
+ },
+ )
+ .focusRequester(focusRequester)
+ .focusable()
+
+/** An extension function for creating [RotaryScrollAdapter] from [ScalingLazyListState] */
+@Composable
+fun ScalingLazyListState.toRotaryScrollAdapter(): RotaryScrollAdapter =
+ remember(this) { ScalingLazyColumnRotaryScrollAdapter(this) }
+
+/** An implementation of rotary scroll adapter for [ScalingLazyColumn] */
+class ScalingLazyColumnRotaryScrollAdapter(
+ override val scrollableState: ScalingLazyListState,
+) : RotaryScrollAdapter {
+
+ /** Calculates an average height of an item by taking an average from visible items height. */
+ override fun averageItemSize(): Float {
+ val visibleItems = scrollableState.layoutInfo.visibleItemsInfo
+ return (visibleItems.fastSumBy { it.unadjustedSize } / visibleItems.size).toFloat()
+ }
+
+ /** Current (centred) item index */
+ override fun currentItemIndex(): Int = scrollableState.centerItemIndex
+
+ /** An offset from the item centre */
+ override fun currentItemOffset(): Float = scrollableState.centerItemScrollOffset.toFloat()
+
+ /** The total count of items in ScalingLazyColumn */
+ override fun totalItemsCount(): Int = scrollableState.layoutInfo.totalItemsCount
+}
+
+/** An adapter which connects scrollableState to Rotary */
+interface RotaryScrollAdapter {
+
+ /** A scrollable state. Used for performing scroll when Rotary events received */
+ val scrollableState: ScrollableState
+
+ /** Average size of an item. Used for estimating the scrollable distance */
+ fun averageItemSize(): Float
+
+ /** A current item index. Used for scrolling */
+ fun currentItemIndex(): Int
+
+ /** An offset from the centre or the border of the current item. */
+ fun currentItemOffset(): Float
+
+ /** The total count of items in [scrollableState] */
+ fun totalItemsCount(): Int
+}
+
+/** Defaults for rotary modifiers */
+object RotaryDefaults {
+
+ /** Returns default [SnapParameters] */
+ val snapParametersDefault: SnapParameters =
+ SnapParameters(
+ snapOffset = 0,
+ thresholdDivider = 1.5f,
+ resistanceFactor = 3f,
+ )
+
+ /** Returns whether the input is Low-res (a bezel) or high-res(a crown/rsb). */
+ @Composable
+ fun isLowResInput(): Boolean =
+ LocalContext.current.packageManager.hasSystemFeature(
+ "android.hardware.rotaryencoder.lowres"
+ )
+
+ /**
+ * Handles scroll with fling.
+ *
+ * @param scrollableState Scrollable state which will be scrolled while receiving rotary events
+ * @param flingBehavior Logic describing Fling behavior. If null - fling will not happen
+ * @param isLowRes Whether the input is Low-res (a bezel) or high-res(a crown/rsb)
+ */
+ @Composable
+ internal fun rememberFlingHandler(
+ scrollableState: ScrollableState,
+ flingBehavior: FlingBehavior? = null,
+ isLowRes: Boolean = isLowResInput(),
+ ): RotaryScrollHandler {
+ val viewConfiguration = ViewConfiguration.get(LocalContext.current)
+
+ return remember(scrollableState, flingBehavior, isLowRes) {
+ // Remove unnecessary recompositions by disabling tracking of changes inside of
+ // this block. This algorithm properly reads all updated values and
+ // don't need recomposition when those values change.
+ Snapshot.withoutReadObservation {
+ debugLog { "isLowRes : $isLowRes" }
+ fun rotaryFlingBehavior() =
+ flingBehavior?.run {
+ RotaryFlingBehavior(
+ scrollableState,
+ flingBehavior,
+ viewConfiguration,
+ flingTimeframe =
+ if (isLowRes) lowResFlingTimeframe else highResFlingTimeframe,
+ )
+ }
+
+ fun scrollBehavior() = RotaryScrollBehavior(scrollableState)
+
+ if (isLowRes) {
+ LowResRotaryScrollHandler(
+ rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+ scrollBehaviorFactory = { scrollBehavior() },
+ )
+ } else {
+ HighResRotaryScrollHandler(
+ rotaryFlingBehaviorFactory = { rotaryFlingBehavior() },
+ scrollBehaviorFactory = { scrollBehavior() },
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles scroll with snap
+ *
+ * @param rotaryScrollAdapter A connection between scrollable objects and rotary events
+ * @param snapParameters Snap parameters
+ */
+ @Composable
+ internal fun rememberSnapHandler(
+ rotaryScrollAdapter: RotaryScrollAdapter,
+ snapParameters: SnapParameters = snapParametersDefault,
+ isLowRes: Boolean = isLowResInput(),
+ ): RotaryScrollHandler {
+ return remember(rotaryScrollAdapter, snapParameters) {
+ // Remove unnecessary recompositions by disabling tracking of changes inside of
+ // this block. This algorithm properly reads all updated values and
+ // don't need recomposition when those values change.
+ Snapshot.withoutReadObservation {
+ debugLog { "isLowRes : $isLowRes" }
+ if (isLowRes) {
+ LowResSnapHandler(
+ snapBehaviourFactory = {
+ RotarySnapBehavior(rotaryScrollAdapter, snapParameters)
+ },
+ )
+ } else {
+ HighResSnapHandler(
+ resistanceFactor = snapParameters.resistanceFactor,
+ thresholdBehaviorFactory = {
+ ThresholdBehavior(
+ rotaryScrollAdapter,
+ snapParameters.thresholdDivider,
+ )
+ },
+ snapBehaviourFactory = {
+ RotarySnapBehavior(rotaryScrollAdapter, snapParameters)
+ },
+ scrollBehaviourFactory = {
+ RotaryScrollBehavior(rotaryScrollAdapter.scrollableState)
+ },
+ )
+ }
+ }
+ }
+ }
+
+ private val lowResFlingTimeframe: Long = 100L
+ private val highResFlingTimeframe: Long = 30L
+}
+
+/**
+ * Parameters used for snapping
+ *
+ * @param snapOffset an optional offset to be applied when snapping the item. After the snap the
+ * snapped items offset will be [snapOffset].
+ */
+class SnapParameters(
+ val snapOffset: Int,
+ val thresholdDivider: Float,
+ val resistanceFactor: Float,
+) {
+ /** Returns a snapping offset in [Dp] */
+ @Composable
+ fun snapOffsetDp(): Dp {
+ return with(LocalDensity.current) { snapOffset.toDp() }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as SnapParameters
+
+ if (snapOffset != other.snapOffset) return false
+ if (thresholdDivider != other.thresholdDivider) return false
+ if (resistanceFactor != other.resistanceFactor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = snapOffset
+ result = 31 * result + thresholdDivider.hashCode()
+ result = 31 * result + resistanceFactor.hashCode()
+ return result
+ }
+}
+
+/** An interface for handling scroll events */
+internal interface RotaryScrollHandler {
+ /**
+ * Handles scrolling events
+ *
+ * @param coroutineScope A scope for performing async actions
+ * @param event A scrollable event from rotary input, containing scrollable delta and timestamp
+ * @param rotaryHaptics
+ */
+ suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ )
+}
+
+/**
+ * Class responsible for Fling behaviour with rotary. It tracks and produces the fling when
+ * necessary
+ */
+internal class RotaryFlingBehavior(
+ private val scrollableState: ScrollableState,
+ private val flingBehavior: FlingBehavior,
+ viewConfiguration: ViewConfiguration,
+ private val flingTimeframe: Long,
+) {
+
+ // A time range during which the fling is valid.
+ // For simplicity it's twice as long as [flingTimeframe]
+ private val timeRangeToFling = flingTimeframe * 2
+
+ // A default fling factor for making fling slower
+ private val flingScaleFactor = 0.7f
+
+ private var previousVelocity = 0f
+
+ private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+ private val minFlingSpeed = viewConfiguration.scaledMinimumFlingVelocity.toFloat()
+ private val maxFlingSpeed = viewConfiguration.scaledMaximumFlingVelocity.toFloat()
+ private var latestEventTimestamp: Long = 0
+
+ private var flingVelocity: Float = 0f
+ private var flingTimestamp: Long = 0
+
+ /** Starts a new fling tracking session with specified timestamp */
+ fun startFlingTracking(timestamp: Long) {
+ rotaryVelocityTracker.start(timestamp)
+ latestEventTimestamp = timestamp
+ previousVelocity = 0f
+ }
+
+ /** Observing new event within a fling tracking session with new timestamp and delta */
+ fun observeEvent(timestamp: Long, delta: Float) {
+ rotaryVelocityTracker.move(timestamp, delta)
+ latestEventTimestamp = timestamp
+ }
+
+ /** Performing fling if necessary and calling [beforeFling] lambda before it is triggered */
+ suspend fun trackFling(beforeFling: () -> Unit) {
+ val currentVelocity = rotaryVelocityTracker.velocity
+ debugLog { "currentVelocity: $currentVelocity" }
+
+ if (abs(currentVelocity) >= abs(previousVelocity)) {
+ flingTimestamp = latestEventTimestamp
+ flingVelocity = currentVelocity * flingScaleFactor
+ }
+ previousVelocity = currentVelocity
+
+ // Waiting for a fixed amount of time before checking the fling
+ delay(flingTimeframe)
+
+ // For making a fling 2 criteria should be met:
+ // 1) no more than
+ // `rangeToFling` ms should pass between last fling detection
+ // and the time of last motion event
+ // 2) flingVelocity should exceed the minFlingSpeed
+ debugLog {
+ "Check fling: flingVelocity: $flingVelocity " +
+ "minFlingSpeed: $minFlingSpeed, maxFlingSpeed: $maxFlingSpeed"
+ }
+ if (
+ latestEventTimestamp - flingTimestamp < timeRangeToFling &&
+ abs(flingVelocity) > minFlingSpeed
+ ) {
+ // Stops scrollAnimationCoroutine because a fling will be performed
+ beforeFling()
+ val velocity = flingVelocity.coerceIn(-maxFlingSpeed, maxFlingSpeed)
+ scrollableState.scroll(MutatePriority.UserInput) {
+ with(flingBehavior) {
+ debugLog { "Flinging with velocity $velocity" }
+ performFling(velocity)
+ }
+ }
+ }
+ }
+}
+
+/**
+ * A rotary event object which contains a [timestamp] of the rotary event and a scrolled [delta].
+ */
+internal data class TimestampedDelta(val timestamp: Long, val delta: Float)
+
+/**
+ * This class does a smooth animation when the scroll by N pixels is done. This animation works well
+ * on Rsb(high-res) and Bezel(low-res) devices.
+ */
+internal class RotaryScrollBehavior(
+ private val scrollableState: ScrollableState,
+) {
+ private var sequentialAnimation = false
+ private var scrollAnimation = AnimationState(0f)
+ private var prevPosition = 0f
+
+ /** Handles scroll event to [targetValue] */
+ suspend fun handleEvent(targetValue: Float) {
+ scrollableState.scroll(MutatePriority.UserInput) {
+ debugLog { "ScrollAnimation value before start: ${scrollAnimation.value}" }
+
+ scrollAnimation.animateTo(
+ targetValue,
+ animationSpec = spring(),
+ sequentialAnimation = sequentialAnimation,
+ ) {
+ val delta = value - prevPosition
+ debugLog { "Animated by $delta, value: $value" }
+ scrollBy(delta)
+ prevPosition = value
+ sequentialAnimation = value != this.targetValue
+ }
+ }
+ }
+}
+
+/**
+ * A helper class for snapping with rotary. Uses animateScrollToItem method for snapping to the Nth
+ * item.
+ */
+internal class RotarySnapBehavior(
+ private val rotaryScrollAdapter: RotaryScrollAdapter,
+ private val snapParameters: SnapParameters,
+) {
+ private var snapTarget: Int = rotaryScrollAdapter.currentItemIndex()
+ private var sequentialSnap: Boolean = false
+
+ private var anim = AnimationState(0f)
+ private var expectedDistance = 0f
+
+ private val defaultStiffness = 200f
+ private var snapTargetUpdated = true
+
+ /**
+ * Preparing snapping. This method should be called before [snapToTargetItem] is called.
+ *
+ * Snapping is done for current + [moveForElements] items.
+ *
+ * If [sequentialSnap] is true, items are summed up together. For example, if
+ * [prepareSnapForItems] is called with [moveForElements] = 2, 3, 5 -> then the snapping will
+ * happen to current + 10 items
+ *
+ * If [sequentialSnap] is false, then [moveForElements] are not summed up together.
+ */
+ fun prepareSnapForItems(moveForElements: Int, sequentialSnap: Boolean) {
+ this.sequentialSnap = sequentialSnap
+ if (sequentialSnap) {
+ snapTarget += moveForElements
+ } else {
+ snapTarget = rotaryScrollAdapter.currentItemIndex() + moveForElements
+ }
+ snapTargetUpdated = true
+ snapTarget = snapTarget.coerceIn(0 until rotaryScrollAdapter.totalItemsCount())
+ }
+
+ /** Performs snapping to the closest item. */
+ suspend fun snapToClosestItem() {
+ // Snapping to the closest item by using performFling method with 0 speed
+ rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+ debugLog { "snap to closest item" }
+ var prevPosition = 0f
+ AnimationState(0f).animateTo(
+ targetValue = -rotaryScrollAdapter.currentItemOffset(),
+ animationSpec = tween(durationMillis = 100, easing = FastOutSlowInEasing),
+ ) {
+ val animDelta = value - prevPosition
+ scrollBy(animDelta)
+ prevPosition = value
+ }
+ snapTarget = rotaryScrollAdapter.currentItemIndex()
+ }
+ }
+
+ /** Returns true if top edge was reached */
+ fun topEdgeReached(): Boolean = snapTarget <= 0
+
+ /** Returns true if bottom edge was reached */
+ fun bottomEdgeReached(): Boolean = snapTarget >= rotaryScrollAdapter.totalItemsCount() - 1
+
+ /** Performs snapping to the specified in [prepareSnapForItems] element */
+ suspend fun snapToTargetItem() {
+ if (sequentialSnap) {
+ anim = anim.copy(0f)
+ } else {
+ anim = AnimationState(0f)
+ }
+ rotaryScrollAdapter.scrollableState.scroll(MutatePriority.UserInput) {
+ // If snapTargetUpdated is true - then the target was updated so we
+ // need to do snap again
+ while (snapTargetUpdated) {
+ snapTargetUpdated = false
+ var latestCenterItem: Int
+ var continueFirstScroll = true
+ debugLog { "snapTarget $snapTarget" }
+ while (continueFirstScroll) {
+ latestCenterItem = rotaryScrollAdapter.currentItemIndex()
+ anim = anim.copy(0f)
+ expectedDistance = expectedDistanceTo(snapTarget, snapParameters.snapOffset)
+ debugLog {
+ "expectedDistance = $expectedDistance, " +
+ "scrollableState.centerItemScrollOffset " +
+ "${rotaryScrollAdapter.currentItemOffset()}"
+ }
+ continueFirstScroll = false
+ var prevPosition = 0f
+
+ anim.animateTo(
+ expectedDistance,
+ animationSpec =
+ SpringSpec(
+ stiffness = defaultStiffness,
+ visibilityThreshold = 0.1f,
+ ),
+ sequentialAnimation = (anim.velocity != 0f),
+ ) {
+ val animDelta = value - prevPosition
+ debugLog {
+ "First animation, value:$value, velocity:$velocity, " +
+ "animDelta:$animDelta"
+ }
+
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) cancelAnimation()
+
+ scrollBy(animDelta)
+ prevPosition = value
+
+ if (latestCenterItem != rotaryScrollAdapter.currentItemIndex()) {
+ continueFirstScroll = true
+ cancelAnimation()
+ return@animateTo
+ }
+
+ debugLog { "centerItemIndex = ${rotaryScrollAdapter.currentItemIndex()}" }
+ if (rotaryScrollAdapter.currentItemIndex() == snapTarget) {
+ debugLog { "Target is visible. Cancelling first animation" }
+ debugLog {
+ "scrollableState.centerItemScrollOffset " +
+ "${rotaryScrollAdapter.currentItemOffset()}"
+ }
+ expectedDistance = -rotaryScrollAdapter.currentItemOffset()
+ continueFirstScroll = false
+ cancelAnimation()
+ return@animateTo
+ }
+ }
+ }
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) continue
+
+ anim = anim.copy(0f)
+ var prevPosition = 0f
+ anim.animateTo(
+ expectedDistance,
+ animationSpec =
+ SpringSpec(
+ stiffness = defaultStiffness,
+ visibilityThreshold = 0.1f,
+ ),
+ sequentialAnimation = (anim.velocity != 0f),
+ ) {
+ // Exit animation if snap target was updated
+ if (snapTargetUpdated) cancelAnimation()
+
+ val animDelta = value - prevPosition
+ debugLog { "Final animation. velocity:$velocity, animDelta:$animDelta" }
+ scrollBy(animDelta)
+ prevPosition = value
+ }
+ }
+ }
+ }
+
+ private fun expectedDistanceTo(index: Int, targetScrollOffset: Int): Float {
+ val averageSize = rotaryScrollAdapter.averageItemSize()
+ val indexesDiff = index - rotaryScrollAdapter.currentItemIndex()
+ debugLog { "Average size $averageSize" }
+ return (averageSize * indexesDiff) + targetScrollOffset -
+ rotaryScrollAdapter.currentItemOffset()
+ }
+}
+
+/**
+ * A modifier which handles rotary events. It accepts ScrollHandler as the input - a class where
+ * main logic about how scroll should be handled is lying
+ */
+internal fun Modifier.rotaryHandler(
+ rotaryScrollHandler: RotaryScrollHandler,
+ reverseDirection: Boolean,
+ rotaryHaptics: RotaryHapticHandler,
+ inspectorInfo: InspectorInfo.() -> Unit,
+): Modifier =
+ this then
+ RotaryHandlerElement(
+ rotaryScrollHandler,
+ reverseDirection,
+ rotaryHaptics,
+ inspectorInfo,
+ )
+
+/**
+ * Batching requests for scrolling events. This function combines all events together (except first)
+ * within specified timeframe. Should help with performance on high-res devices.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+internal fun Flow<TimestampedDelta>.batchRequestsWithinTimeframe(
+ timeframe: Long
+): Flow<TimestampedDelta> {
+ var delta = 0f
+ var lastTimestamp = -timeframe
+ return if (timeframe == 0L) {
+ this
+ } else {
+ this.transformLatest {
+ delta += it.delta
+ debugLog { "Batching requests. delta:$delta" }
+ if (lastTimestamp + timeframe <= it.timestamp) {
+ lastTimestamp = it.timestamp
+ debugLog { "No events before, delta= $delta" }
+ emit(TimestampedDelta(it.timestamp, delta))
+ } else {
+ delay(timeframe)
+ debugLog { "After delay, delta= $delta" }
+ if (delta > 0f) {
+ emit(TimestampedDelta(it.timestamp, delta))
+ }
+ }
+ delta = 0f
+ }
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) without snapping and with or without fling A list is scrolled
+ * by the number of pixels received from the rotary device.
+ *
+ * This class is a little bit different from LowResScrollHandler class - it has a filtering for
+ * events which are coming with wrong sign ( this happens to rsb devices, especially at the end of
+ * the scroll)
+ *
+ * This scroll handler supports fling. It can be set with [RotaryFlingBehavior].
+ */
+internal class HighResRotaryScrollHandler(
+ private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+ private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+ private val hapticsThreshold: Long = 50,
+) : RotaryScrollHandler {
+
+ // This constant is specific for high-res devices. Because that input values
+ // can sometimes come with different sign, we have to filter them in this threshold
+ private val gestureThresholdTime = 200L
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var flingJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var rotaryScrollDistance = 0f
+
+ private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+ private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+ val isOppositeScrollValue = isOppositeValueAfterScroll(event.delta)
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking(time)
+ rotaryScrollDistance = event.delta
+ } else {
+ // Due to the physics of Rotary side button, some events might come
+ // with an opposite axis value - either at the start or at the end of the motion.
+ // We don't want to use these values for fling calculations.
+ if (!isOppositeScrollValue) {
+ rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+ } else {
+ debugLog { "Opposite value after scroll :${event.delta}" }
+ }
+ rotaryScrollDistance += event.delta
+ }
+
+ scrollJob.cancel()
+
+ rotaryHaptics.handleScrollHaptic(event.delta)
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ previousScrollEventTime = time
+ scrollJob = coroutineScope.async { scrollBehavior.handleEvent(rotaryScrollDistance) }
+
+ if (rotaryFlingBehavior != null) {
+ flingJob.cancel()
+ flingJob =
+ coroutineScope.async {
+ rotaryFlingBehavior?.trackFling(
+ beforeFling = {
+ debugLog { "Calling before fling section" }
+ scrollJob.cancel()
+ scrollBehavior = scrollBehaviorFactory()
+ }
+ )
+ }
+ }
+ }
+
+ private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+ rotaryScrollDistance * delta < 0f && (abs(delta) < abs(rotaryScrollDistance))
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking(timestamp: Long) {
+ scrollBehavior = scrollBehaviorFactory()
+ rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+ rotaryFlingBehavior?.startFlingTracking(timestamp)
+ }
+}
+
+/**
+ * A scroll handler for Bezel(low-res) without snapping. This scroll handler supports fling. It can
+ * be set with RotaryFlingBehavior.
+ */
+internal class LowResRotaryScrollHandler(
+ private val rotaryFlingBehaviorFactory: () -> RotaryFlingBehavior?,
+ private val scrollBehaviorFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+
+ private val gestureThresholdTime = 200L
+ private var previousScrollEventTime = 0L
+ private var rotaryScrollDistance = 0f
+
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var flingJob: Job = CompletableDeferred<Unit>()
+
+ private var rotaryFlingBehavior: RotaryFlingBehavior? = rotaryFlingBehaviorFactory()
+ private var scrollBehavior: RotaryScrollBehavior = scrollBehaviorFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ resetTracking(time)
+ rotaryScrollDistance = event.delta
+ } else {
+ rotaryFlingBehavior?.observeEvent(event.timestamp, event.delta)
+ rotaryScrollDistance += event.delta
+ }
+
+ scrollJob.cancel()
+ flingJob.cancel()
+
+ rotaryHaptics.handleScrollHaptic(event.delta)
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ previousScrollEventTime = time
+ scrollJob = coroutineScope.async { scrollBehavior.handleEvent(rotaryScrollDistance) }
+
+ flingJob =
+ coroutineScope.async {
+ rotaryFlingBehavior?.trackFling(
+ beforeFling = {
+ debugLog { "Calling before fling section" }
+ scrollJob.cancel()
+ scrollBehavior = scrollBehaviorFactory()
+ },
+ )
+ }
+ }
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking(timestamp: Long) {
+ scrollBehavior = scrollBehaviorFactory()
+ debugLog { "Velocity tracker reset" }
+ rotaryFlingBehavior = rotaryFlingBehaviorFactory()
+ rotaryFlingBehavior?.startFlingTracking(timestamp)
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling Snapping happens after a
+ * threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class HighResSnapHandler(
+ private val resistanceFactor: Float,
+ private val thresholdBehaviorFactory: () -> ThresholdBehavior,
+ private val snapBehaviourFactory: () -> RotarySnapBehavior,
+ private val scrollBehaviourFactory: () -> RotaryScrollBehavior,
+) : RotaryScrollHandler {
+ private val gestureThresholdTime = 200L
+ private val snapDelay = 100L
+ private val maxSnapsPerEvent = 2
+
+ private var scrollJob: Job = CompletableDeferred<Unit>()
+ private var snapJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var snapAccumulator = 0f
+ private var rotaryScrollDistance = 0f
+ private var scrollInProgress = false
+
+ private var snapBehaviour = snapBehaviourFactory()
+ private var scrollBehaviour = scrollBehaviourFactory()
+ private var thresholdBehavior = thresholdBehaviorFactory()
+
+ private val scrollEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.5f, 1.0f)
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking()
+ snapJob.cancel()
+ snapBehaviour = snapBehaviourFactory()
+ scrollBehaviour = scrollBehaviourFactory()
+ thresholdBehavior = thresholdBehaviorFactory()
+ thresholdBehavior.startThresholdTracking(time)
+ snapAccumulator = 0f
+ rotaryScrollDistance = 0f
+ }
+
+ if (!isOppositeValueAfterScroll(event.delta)) {
+ thresholdBehavior.observeEvent(event.timestamp, event.delta)
+ } else {
+ debugLog { "Opposite value after scroll :${event.delta}" }
+ }
+
+ thresholdBehavior.applySmoothing()
+ val snapThreshold = thresholdBehavior.snapThreshold()
+
+ snapAccumulator += event.delta
+ if (!snapJob.isActive) {
+ val resistanceCoeff =
+ 1 - scrollEasing.transform(rotaryScrollDistance.absoluteValue / snapThreshold)
+ rotaryScrollDistance += event.delta * resistanceCoeff
+ }
+
+ debugLog { "Snap accumulator: $snapAccumulator" }
+ debugLog { "Rotary scroll distance: $rotaryScrollDistance" }
+
+ debugLog { "snapThreshold: $snapThreshold" }
+ previousScrollEventTime = time
+
+ if (abs(snapAccumulator) > snapThreshold) {
+ scrollInProgress = false
+ scrollBehaviour = scrollBehaviourFactory()
+ scrollJob.cancel()
+
+ val snapDistance =
+ (snapAccumulator / snapThreshold)
+ .toInt()
+ .coerceIn(-maxSnapsPerEvent..maxSnapsPerEvent)
+ snapAccumulator -= snapThreshold * snapDistance
+ val sequentialSnap = snapJob.isActive
+
+ debugLog {
+ "Snap threshold reached: snapDistance:$snapDistance, " +
+ "sequentialSnap: $sequentialSnap, " +
+ "snap accumulator remaining: $snapAccumulator"
+ }
+ if (
+ (!snapBehaviour.topEdgeReached() && snapDistance < 0) ||
+ (!snapBehaviour.bottomEdgeReached() && snapDistance > 0)
+ ) {
+ rotaryHaptics.handleSnapHaptic(event.delta)
+ }
+
+ snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+ if (!snapJob.isActive) {
+ snapJob.cancel()
+ snapJob =
+ coroutineScope.async {
+ debugLog { "Snap started" }
+ try {
+ snapBehaviour.snapToTargetItem()
+ } finally {
+ debugLog { "Snap called finally" }
+ }
+ }
+ }
+ rotaryScrollDistance = 0f
+ } else {
+ if (!snapJob.isActive) {
+ scrollJob.cancel()
+ debugLog { "Scrolling for $rotaryScrollDistance/$resistanceFactor px" }
+ scrollJob =
+ coroutineScope.async {
+ scrollBehaviour.handleEvent(rotaryScrollDistance / resistanceFactor)
+ }
+ delay(snapDelay)
+ scrollInProgress = false
+ scrollBehaviour = scrollBehaviourFactory()
+ rotaryScrollDistance = 0f
+ snapAccumulator = 0f
+ snapBehaviour.prepareSnapForItems(0, false)
+
+ snapJob.cancel()
+ snapJob = coroutineScope.async { snapBehaviour.snapToClosestItem() }
+ }
+ }
+ }
+
+ private fun isOppositeValueAfterScroll(delta: Float): Boolean =
+ sign(rotaryScrollDistance) * sign(delta) == -1f && (abs(delta) < abs(rotaryScrollDistance))
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking() {
+ scrollInProgress = true
+ }
+}
+
+/**
+ * A scroll handler for RSB(high-res) with snapping and without fling Snapping happens after a
+ * threshold is reached ( set in [RotarySnapBehavior])
+ *
+ * This scroll handler doesn't support fling.
+ */
+internal class LowResSnapHandler(
+ private val snapBehaviourFactory: () -> RotarySnapBehavior,
+) : RotaryScrollHandler {
+ private val gestureThresholdTime = 200L
+
+ private var snapJob: Job = CompletableDeferred<Unit>()
+
+ private var previousScrollEventTime = 0L
+ private var snapAccumulator = 0f
+ private var scrollInProgress = false
+
+ private var snapBehaviour = snapBehaviourFactory()
+
+ override suspend fun handleScrollEvent(
+ coroutineScope: CoroutineScope,
+ event: TimestampedDelta,
+ rotaryHaptics: RotaryHapticHandler,
+ ) {
+ val time = event.timestamp
+
+ if (isNewScrollEvent(time)) {
+ debugLog { "New scroll event" }
+ resetTracking()
+ snapJob.cancel()
+ snapBehaviour = snapBehaviourFactory()
+ snapAccumulator = 0f
+ }
+
+ snapAccumulator += event.delta
+
+ debugLog { "Snap accumulator: $snapAccumulator" }
+
+ previousScrollEventTime = time
+
+ if (abs(snapAccumulator) > 1f) {
+ scrollInProgress = false
+
+ val snapDistance = sign(snapAccumulator).toInt()
+ rotaryHaptics.handleSnapHaptic(event.delta)
+ val sequentialSnap = snapJob.isActive
+ debugLog {
+ "Snap threshold reached: snapDistance:$snapDistance, " +
+ "sequentialSnap: $sequentialSnap, " +
+ "snap accumulator: $snapAccumulator"
+ }
+
+ snapBehaviour.prepareSnapForItems(snapDistance, sequentialSnap)
+ if (!snapJob.isActive) {
+ snapJob.cancel()
+ snapJob =
+ coroutineScope.async {
+ debugLog { "Snap started" }
+ try {
+ snapBehaviour.snapToTargetItem()
+ } finally {
+ debugLog { "Snap called finally" }
+ }
+ }
+ }
+ snapAccumulator = 0f
+ }
+ }
+
+ private fun isNewScrollEvent(timestamp: Long): Boolean {
+ val timeDelta = timestamp - previousScrollEventTime
+ return previousScrollEventTime == 0L || timeDelta > gestureThresholdTime
+ }
+
+ private fun resetTracking() {
+ scrollInProgress = true
+ }
+}
+
+internal class ThresholdBehavior(
+ private val rotaryScrollAdapter: RotaryScrollAdapter,
+ private val thresholdDivider: Float,
+ private val minVelocity: Float = 300f,
+ private val maxVelocity: Float = 3000f,
+ private val smoothingConstant: Float = 0.4f,
+) {
+ private val thresholdDividerEasing: Easing = CubicBezierEasing(0.5f, 0.0f, 0.5f, 1.0f)
+
+ private val rotaryVelocityTracker = RotaryVelocityTracker()
+
+ private var smoothedVelocity = 0f
+
+ fun startThresholdTracking(time: Long) {
+ rotaryVelocityTracker.start(time)
+ smoothedVelocity = 0f
+ }
+
+ fun observeEvent(timestamp: Long, delta: Float) {
+ rotaryVelocityTracker.move(timestamp, delta)
+ }
+
+ fun applySmoothing() {
+ if (rotaryVelocityTracker.velocity != 0.0f) {
+ // smooth the velocity
+ smoothedVelocity =
+ exponentialSmoothing(
+ currentVelocity = rotaryVelocityTracker.velocity.absoluteValue,
+ prevVelocity = smoothedVelocity,
+ smoothingConstant = smoothingConstant,
+ )
+ }
+ debugLog { "rotaryVelocityTracker velocity: ${rotaryVelocityTracker.velocity}" }
+ debugLog { "SmoothedVelocity: $smoothedVelocity" }
+ }
+
+ fun snapThreshold(): Float {
+ val thresholdDividerFraction =
+ thresholdDividerEasing.transform(
+ inverseLerp(
+ minVelocity,
+ maxVelocity,
+ smoothedVelocity,
+ ),
+ )
+ return rotaryScrollAdapter.averageItemSize() /
+ lerp(
+ 1f,
+ thresholdDivider,
+ thresholdDividerFraction,
+ )
+ }
+
+ private fun exponentialSmoothing(
+ currentVelocity: Float,
+ prevVelocity: Float,
+ smoothingConstant: Float,
+ ): Float = smoothingConstant * currentVelocity + (1 - smoothingConstant) * prevVelocity
+}
+
+private data class RotaryHandlerElement(
+ private val rotaryScrollHandler: RotaryScrollHandler,
+ private val reverseDirection: Boolean,
+ private val rotaryHaptics: RotaryHapticHandler,
+ private val inspectorInfo: InspectorInfo.() -> Unit,
+) : ModifierNodeElement<RotaryInputNode>() {
+ override fun create(): RotaryInputNode =
+ RotaryInputNode(
+ rotaryScrollHandler,
+ reverseDirection,
+ rotaryHaptics,
+ )
+
+ override fun update(node: RotaryInputNode) {
+ debugLog { "Update launched!" }
+ node.rotaryScrollHandler = rotaryScrollHandler
+ node.reverseDirection = reverseDirection
+ node.rotaryHaptics = rotaryHaptics
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ inspectorInfo()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || this::class != other::class) return false
+
+ other as RotaryHandlerElement
+
+ if (rotaryScrollHandler != other.rotaryScrollHandler) return false
+ if (reverseDirection != other.reverseDirection) return false
+ if (rotaryHaptics != other.rotaryHaptics) return false
+ if (inspectorInfo != other.inspectorInfo) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = rotaryScrollHandler.hashCode()
+ result = 31 * result + reverseDirection.hashCode()
+ result = 31 * result + rotaryHaptics.hashCode()
+ result = 31 * result + inspectorInfo.hashCode()
+ return result
+ }
+}
+
+private class RotaryInputNode(
+ var rotaryScrollHandler: RotaryScrollHandler,
+ var reverseDirection: Boolean,
+ var rotaryHaptics: RotaryHapticHandler,
+) : RotaryInputModifierNode, Modifier.Node() {
+
+ val channel = Channel<TimestampedDelta>(capacity = Channel.CONFLATED)
+ val flow = channel.receiveAsFlow()
+
+ override fun onAttach() {
+ coroutineScope.launch {
+ flow.collectLatest {
+ debugLog {
+ "Scroll event received: " + "delta:${it.delta}, timestamp:${it.timestamp}"
+ }
+ rotaryScrollHandler.handleScrollEvent(this, it, rotaryHaptics)
+ }
+ }
+ }
+
+ override fun onRotaryScrollEvent(event: RotaryScrollEvent): Boolean = false
+
+ override fun onPreRotaryScrollEvent(event: RotaryScrollEvent): Boolean {
+ debugLog { "onPreRotaryScrollEvent" }
+ channel.trySend(
+ TimestampedDelta(
+ event.uptimeMillis,
+ event.verticalScrollPixels * if (reverseDirection) -1f else 1f,
+ ),
+ )
+ return true
+ }
+}
+
+private fun inverseLerp(start: Float, stop: Float, value: Float): Float {
+ return ((value - start) / (stop - start)).coerceIn(0f, 1f)
+}
+
+/** Debug logging that can be enabled. */
+private const val DEBUG = false
+
+private inline fun debugLog(generateMsg: () -> String) {
+ if (DEBUG) {
+ println("RotaryScroll: ${generateMsg()}")
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/RotaryVelocityTracker.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/RotaryVelocityTracker.kt
new file mode 100644
index 000000000..1719ecef3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/rotaryinput/RotaryVelocityTracker.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.elements.rotaryinput
+
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+
+// This file is a copy of RotaryVelocityTracker.kt from Horologist (go/horologist),
+// remove it once Wear Compose 1.4 is landed (b/325560444).
+
+/** A wrapper around VelocityTracker1D to provide support for rotary input. */
+class RotaryVelocityTracker {
+ private var velocityTracker: VelocityTracker1D = VelocityTracker1D(true)
+
+ /** Retrieve the last computed velocity. */
+ val velocity: Float
+ get() = velocityTracker.calculateVelocity()
+
+ /** Start tracking motion. */
+ fun start(currentTime: Long) {
+ velocityTracker.resetTracking()
+ velocityTracker.addDataPoint(currentTime, 0f)
+ }
+
+ /** Continue tracking motion as the input rotates. */
+ fun move(currentTime: Long, delta: Float) {
+ velocityTracker.addDataPoint(currentTime, delta)
+ }
+
+ /** Stop tracking motion. */
+ fun end() {
+ velocityTracker.resetTracking()
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionConfirmDialogViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionConfirmDialogViewModel.kt
new file mode 100644
index 000000000..ffcc4f7ec
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionConfirmDialogViewModel.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.permissioncontroller.permission.ui.wear.model
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel
+import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs
+
+class AppPermissionConfirmDialogViewModel : ViewModel() {
+ /** A livedata which stores whether confirmation dialog is visible. */
+ val showConfirmDialogLiveData = MutableLiveData<Boolean>()
+
+ /** Arguments for a confirmation dialog. */
+ var confirmDialogArgs: ConfirmDialogArgs? = null
+
+ /** A livedata which stores whether confirmation dialog is visible. */
+ val showAdvancedConfirmDialogLiveData = MutableLiveData<Boolean>()
+
+ /** Arguments for an advanced confirmation dialog. */
+ var advancedConfirmDialogArgs: AdvancedConfirmDialogArgs? = null
+
+ init {
+ showConfirmDialogLiveData.value = false
+ showAdvancedConfirmDialogLiveData.value = false
+ }
+}
+
+/** Factory for an AppPermissionConfirmDialogViewModel */
+class AppPermissionConfirmDialogViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return AppPermissionConfirmDialogViewModel() as T
+ }
+}
+
+/** */
+data class ConfirmDialogArgs(
+ val messageId: Int,
+ val changeRequest: AppPermissionViewModel.ChangeRequest,
+ val buttonPressed: Int,
+ val oneTime: Boolean
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionGroupsRevokeDialogViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionGroupsRevokeDialogViewModel.kt
new file mode 100644
index 000000000..d383af7b0
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/AppPermissionGroupsRevokeDialogViewModel.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.permissioncontroller.permission.ui.wear.model
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+class AppPermissionGroupsRevokeDialogViewModel : ViewModel() {
+ /** A livedata which stores whether the dialog is visible. */
+ val showDialogLiveData = MutableLiveData<Boolean>()
+ var hasConfirmedRevoke: Boolean = false
+ var revokeDialogArgs: RevokeDialogArgs? = null
+
+ init {
+ showDialogLiveData.value = false
+ }
+
+ fun dismissDialog() {
+ showDialogLiveData.value = false
+ revokeDialogArgs = null
+ }
+}
+
+/** Factory for an AppPermissionGroupsRevokeDialogViewModel */
+class AppPermissionGroupsRevokeDialogViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return AppPermissionGroupsRevokeDialogViewModel() as T
+ }
+}
+
+data class RevokeDialogArgs(
+ val messageId: Int,
+ val onOkButtonClick: () -> Unit,
+ val onCancelButtonClick: () -> Unit
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt
new file mode 100644
index 000000000..85e9eaef2
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearAppPermissionUsagesViewModel.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.model
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage
+
+class WearAppPermissionUsagesViewModel : ViewModel() {
+ val appPermissionUsages = MutableLiveData<List<AppPermissionUsage>>()
+}
+
+/** Factory for a WearAppPermissionGroupsViewModel */
+class WearAppPermissionUsagesViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return WearAppPermissionUsagesViewModel() as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt
new file mode 100644
index 000000000..54a6e7c9f
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearGrantPermissionsViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.model
+
+import android.graphics.drawable.Drawable
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+class WearGrantPermissionsViewModel : ViewModel() {
+ /** A livedata which stores the permission group name. */
+ val groupNameLiveData = MutableLiveData<String>()
+
+ /** A livedata which stores the permission group icon. */
+ val iconLiveData = MutableLiveData<Drawable?>()
+
+ /** A livedata which stores the permission group message. */
+ val groupMessageLiveData = MutableLiveData<String>()
+
+ /** A livedata which stores the permission group detail message. */
+ val detailMessageLiveData = MutableLiveData<CharSequence?>()
+
+ /** A livedata which stores the permission group location-granularity visibilities. */
+ val locationVisibilitiesLiveData = MutableLiveData<List<Boolean>>()
+
+ /** A livedata which stores weather the precise location toggle chip is checked. */
+ val preciseLocationCheckedLiveData = MutableLiveData<Boolean>()
+
+ /** A livedata which stores the permission group button visibilities. */
+ val buttonVisibilitiesLiveData = MutableLiveData<List<Boolean>>()
+
+ init {
+ groupNameLiveData.value = ""
+ iconLiveData.value = null
+ groupMessageLiveData.value = ""
+ detailMessageLiveData.value = null
+ locationVisibilitiesLiveData.value = emptyList()
+ preciseLocationCheckedLiveData.value = false
+ buttonVisibilitiesLiveData.value = emptyList()
+ }
+}
+
+/** Factory for a WearGrantPermissionsViewModel */
+class WearGrantPermissionsViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return WearGrantPermissionsViewModel() as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt
new file mode 100644
index 000000000..380c3cfee
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearPermissionUsageViewModel.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.model
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState
+
+class WearPermissionUsageViewModel(
+ permissionUsagesUiState: PermissionUsagesUiState?,
+ showSystemApps: Boolean,
+ show7DaysData: Boolean
+) : ViewModel() {
+ val permissionUsagesUiStateLiveData = MutableLiveData(permissionUsagesUiState)
+ /** A livedata which stores [BasePermissionUsageViewModel.getShowSystemApps()]. */
+ val showSystemAppsLiveData = MutableLiveData(showSystemApps)
+
+ /** A livedata which stores [BasePermissionUsageViewModel.getShow7DaysData()]. */
+ val show7DaysLiveData = MutableLiveData(show7DaysData)
+
+ fun updatePermissionUsagesUiStateLiveData(newUiData: PermissionUsagesUiState?) {
+ permissionUsagesUiStateLiveData.value = newUiData
+ }
+}
+
+/** Factory for a WearPermissionsUsageViewModel */
+class WearPermissionUsageViewModelFactory(val viewModel: PermissionUsageViewModel) :
+ ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST")
+ return WearPermissionUsageViewModel(
+ viewModel.permissionUsagesUiLiveData.value,
+ viewModel.getShowSystemApps(),
+ viewModel.getShow7DaysData()
+ )
+ as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearUnusedAppsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearUnusedAppsViewModel.kt
new file mode 100644
index 000000000..a2d987e02
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/model/WearUnusedAppsViewModel.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.ui.wear.model
+
+import android.graphics.drawable.Drawable
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod
+
+class WearUnusedAppsViewModel : ViewModel() {
+ /** A livedata which indicates if the loading animation should be showed. */
+ val loadingLiveData = MutableLiveData<Boolean>()
+
+ /** A livedata which stores unused period category visibilities. */
+ val unusedPeriodCategoryVisibilitiesLiveData = MutableLiveData<List<Boolean>>()
+
+ /** A livedata which indicates if the info massage category is visible or not. */
+ val infoMsgCategoryVisibilityLiveData = MutableLiveData<Boolean>()
+
+ /** A livedata which stores a map of unused apps group by UnusedPeriod. */
+ val unusedAppChipsLiveData =
+ MutableLiveData<Map<UnusedPeriod, Map<String, UnusedAppChip>>>()
+
+ data class UnusedAppChip(
+ val label: String,
+ val summary: String?,
+ val icon: Drawable?,
+ val contentDescription: String?,
+ val onClick: () -> Unit,
+ )
+
+ init {
+ loadingLiveData.value = true
+ unusedPeriodCategoryVisibilitiesLiveData.value = emptyList()
+ infoMsgCategoryVisibilityLiveData.value = false
+ unusedAppChipsLiveData.value = mutableMapOf()
+ }
+}
+
+/** Factory for a WearUnusedAppsViewModel */
+class WearUnusedAppsViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return WearUnusedAppsViewModel() as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt
new file mode 100644
index 000000000..933cf19f9
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTheme.kt
@@ -0,0 +1,131 @@
+package com.android.permissioncontroller.permission.ui.wear.theme
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.DeviceFontFamilyName
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.wear.compose.material.Colors
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Typography
+import com.android.permissioncontroller.R
+
+/** The Material 3 Theme Wrapper for Supporting RRO. */
+@Composable
+fun WearPermissionTheme(content: @Composable () -> Unit) {
+ val context = LocalContext.current
+ val colors =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ overlayColors(context)
+ .copy(error = MaterialTheme.colors.error, onError = MaterialTheme.colors.onError)
+ } else {
+ MaterialTheme.colors
+ }
+ MaterialTheme(colors = colors, typography = deviceDefaultTypography(context), content = content)
+}
+
+/**
+ * Creates a dynamic color maps that can be overlaid. 100 - Lightest shade; 0 - Darkest Shade; In
+ * wear we only support dark theme for the time being. Thus the fill colors and variants are dark
+ * and anything on top is light. We will use this custom redirection until wear compose material
+ * supports color scheming.
+ *
+ * The mapping is best case match on wear material color tokens from
+ * /android/clockwork/common/wearable/wearmaterial/color/res/values/color-tokens.xml
+ *
+ * @param context The context required to get system resource data.
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+@VisibleForTesting
+internal fun overlayColors(context: Context): Colors {
+ val tonalPalette = dynamicTonalPalette(context)
+ return Colors(
+ background = Color.Black,
+ onBackground = Color.White,
+ primary = tonalPalette.primary90,
+ primaryVariant = tonalPalette.primary80,
+ onPrimary = tonalPalette.primary10,
+ secondary = tonalPalette.tertiary90,
+ secondaryVariant = tonalPalette.tertiary60,
+ onSecondary = tonalPalette.tertiary10,
+ surface = tonalPalette.neutral20,
+ onSurface = tonalPalette.neutral95,
+ onSurfaceVariant = tonalPalette.neutralVariant80,
+ )
+}
+
+private fun fontFamily(context: Context, @StringRes id: Int): FontFamily {
+ val typefaceName = context.resources.getString(id)
+ val font = Font(familyName = DeviceFontFamilyName(typefaceName))
+ return FontFamily(font)
+}
+
+/*
+ Only customizes font family. The material 3 roles to 2.5 are mapped to the best case matching of
+ google3/java/com/google/android/wearable/libraries/compose/theme/GoogleMaterialTheme.kt
+*/
+internal fun deviceDefaultTypography(context: Context): Typography {
+ val defaultTypography = Typography()
+ return Typography(
+ display1 =
+ defaultTypography.display1.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_display_1_font_family)
+ ),
+ display2 =
+ defaultTypography.display2.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_display_2_font_family)
+ ),
+ display3 =
+ defaultTypography.display3.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_display_3_font_family)
+ ),
+ title1 =
+ defaultTypography.title1.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_title_1_font_family)
+ ),
+ title2 =
+ defaultTypography.title2.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_title_2_font_family)
+ ),
+ title3 =
+ defaultTypography.title3.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_title_3_font_family)
+ ),
+ body1 =
+ defaultTypography.body1.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_body_1_font_family)
+ ),
+ body2 =
+ defaultTypography.body2.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_body_2_font_family)
+ ),
+ button =
+ defaultTypography.button.copy(
+ fontFamily = fontFamily(context, R.string.wear_material_compose_button_font_family)
+ ),
+ caption1 =
+ defaultTypography.caption1.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_caption_1_font_family)
+ ),
+ caption2 =
+ defaultTypography.caption2.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_caption_2_font_family)
+ ),
+ caption3 =
+ defaultTypography.caption3.copy(
+ fontFamily =
+ fontFamily(context, R.string.wear_material_compose_caption_3_font_family)
+ ),
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTonalPalette.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTonalPalette.kt
new file mode 100644
index 000000000..a86af8b3d
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/theme/WearPermissionTonalPalette.kt
@@ -0,0 +1,191 @@
+@file:Suppress("unused")
+
+package com.android.permissioncontroller.permission.ui.wear.theme
+
+import android.R
+import android.content.Context
+import android.os.Build
+import androidx.annotation.ColorRes
+import androidx.annotation.DoNotInline
+import androidx.annotation.RequiresApi
+import androidx.compose.ui.graphics.Color
+
+/**
+ * Tonal Palette structure in Material.
+ *
+ * A tonal palette is comprised of 5 tonal ranges. Each tonal range includes the 13 stops, or tonal
+ * swatches.
+ *
+ * Tonal range names are:
+ * - Neutral (N)
+ * - Neutral variant (NV)
+ * - Primary (P)
+ * - Secondary (S)
+ * - Tertiary (T)
+ */
+internal class WearPermissionTonalPalette(
+ // The neutral tonal range.
+ val neutral100: Color,
+ val neutral99: Color,
+ val neutral95: Color,
+ val neutral90: Color,
+ val neutral80: Color,
+ val neutral70: Color,
+ val neutral60: Color,
+ val neutral50: Color,
+ val neutral40: Color,
+ val neutral30: Color,
+ val neutral20: Color,
+ val neutral10: Color,
+ val neutral0: Color,
+
+ // The neutral variant tonal range, sometimes called "neutral 2"
+ val neutralVariant100: Color,
+ val neutralVariant99: Color,
+ val neutralVariant95: Color,
+ val neutralVariant90: Color,
+ val neutralVariant80: Color,
+ val neutralVariant70: Color,
+ val neutralVariant60: Color,
+ val neutralVariant50: Color,
+ val neutralVariant40: Color,
+ val neutralVariant30: Color,
+ val neutralVariant20: Color,
+ val neutralVariant10: Color,
+ val neutralVariant0: Color,
+
+ // The primary tonal range, also known as accent 1
+ val primary100: Color,
+ val primary99: Color,
+ val primary95: Color,
+ val primary90: Color,
+ val primary80: Color,
+ val primary70: Color,
+ val primary60: Color,
+ val primary50: Color,
+ val primary40: Color,
+ val primary30: Color,
+ val primary20: Color,
+ val primary10: Color,
+ val primary0: Color,
+
+ // The Secondary tonal range, also know as accent 2
+ val secondary100: Color,
+ val secondary99: Color,
+ val secondary95: Color,
+ val secondary90: Color,
+ val secondary80: Color,
+ val secondary70: Color,
+ val secondary60: Color,
+ val secondary50: Color,
+ val secondary40: Color,
+ val secondary30: Color,
+ val secondary20: Color,
+ val secondary10: Color,
+ val secondary0: Color,
+
+ // The tertiary tonal range, also known as accent 3
+ val tertiary100: Color,
+ val tertiary99: Color,
+ val tertiary95: Color,
+ val tertiary90: Color,
+ val tertiary80: Color,
+ val tertiary70: Color,
+ val tertiary60: Color,
+ val tertiary50: Color,
+ val tertiary40: Color,
+ val tertiary30: Color,
+ val tertiary20: Color,
+ val tertiary10: Color,
+ val tertiary0: Color,
+)
+/** Dynamic colors for wear compose material to support resource overlay. */
+@RequiresApi(Build.VERSION_CODES.S)
+// TODO: once we have proper support for this on Wear 6+, we will do something similar to
+// https://source.corp.google.com/h/android/platform/superproject/+/androidx-main:frameworks/support/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/DynamicTonalPalette.android.kt;l=307-362?q=dynamicTonalPalette&sq=repo:android%2Fplatform%2Fsuperproject%20b:androidx-main
+// Tracking Bug: b/270720571
+internal fun dynamicTonalPalette(context: Context) =
+ WearPermissionTonalPalette(
+ // The neutral tonal range from the generated dynamic color palette.
+ neutral100 = ColorResourceHelper.getColor(context, R.color.system_neutral1_0),
+ neutral99 = ColorResourceHelper.getColor(context, R.color.system_neutral1_10),
+ neutral95 = ColorResourceHelper.getColor(context, R.color.system_neutral1_50),
+ neutral90 = ColorResourceHelper.getColor(context, R.color.system_neutral1_100),
+ neutral80 = ColorResourceHelper.getColor(context, R.color.system_neutral1_200),
+ neutral70 = ColorResourceHelper.getColor(context, R.color.system_neutral1_300),
+ neutral60 = ColorResourceHelper.getColor(context, R.color.system_neutral1_400),
+ neutral50 = ColorResourceHelper.getColor(context, R.color.system_neutral1_500),
+ neutral40 = ColorResourceHelper.getColor(context, R.color.system_neutral1_600),
+ neutral30 = ColorResourceHelper.getColor(context, R.color.system_neutral1_700),
+ neutral20 = ColorResourceHelper.getColor(context, R.color.system_neutral1_800),
+ neutral10 = ColorResourceHelper.getColor(context, R.color.system_neutral1_900),
+ neutral0 = ColorResourceHelper.getColor(context, R.color.system_neutral1_1000),
+
+ // The neutral variant tonal range, sometimes called "neutral 2", from the
+ // generated dynamic color palette.
+ neutralVariant100 = ColorResourceHelper.getColor(context, R.color.system_neutral2_0),
+ neutralVariant99 = ColorResourceHelper.getColor(context, R.color.system_neutral2_10),
+ neutralVariant95 = ColorResourceHelper.getColor(context, R.color.system_neutral2_50),
+ neutralVariant90 = ColorResourceHelper.getColor(context, R.color.system_neutral2_100),
+ neutralVariant80 = ColorResourceHelper.getColor(context, R.color.system_neutral2_200),
+ neutralVariant70 = ColorResourceHelper.getColor(context, R.color.system_neutral2_300),
+ neutralVariant60 = ColorResourceHelper.getColor(context, R.color.system_neutral2_400),
+ neutralVariant50 = ColorResourceHelper.getColor(context, R.color.system_neutral2_500),
+ neutralVariant40 = ColorResourceHelper.getColor(context, R.color.system_neutral2_600),
+ neutralVariant30 = ColorResourceHelper.getColor(context, R.color.system_neutral2_700),
+ neutralVariant20 = ColorResourceHelper.getColor(context, R.color.system_neutral2_800),
+ neutralVariant10 = ColorResourceHelper.getColor(context, R.color.system_neutral2_900),
+ neutralVariant0 = ColorResourceHelper.getColor(context, R.color.system_neutral2_1000),
+
+ // The primary tonal range from the generated dynamic color palette.
+ primary100 = ColorResourceHelper.getColor(context, R.color.system_accent1_0),
+ primary99 = ColorResourceHelper.getColor(context, R.color.system_accent1_10),
+ primary95 = ColorResourceHelper.getColor(context, R.color.system_accent1_50),
+ primary90 = ColorResourceHelper.getColor(context, R.color.system_accent1_100),
+ primary80 = ColorResourceHelper.getColor(context, R.color.system_accent1_200),
+ primary70 = ColorResourceHelper.getColor(context, R.color.system_accent1_300),
+ primary60 = ColorResourceHelper.getColor(context, R.color.system_accent1_400),
+ primary50 = ColorResourceHelper.getColor(context, R.color.system_accent1_500),
+ primary40 = ColorResourceHelper.getColor(context, R.color.system_accent1_600),
+ primary30 = ColorResourceHelper.getColor(context, R.color.system_accent1_700),
+ primary20 = ColorResourceHelper.getColor(context, R.color.system_accent1_800),
+ primary10 = ColorResourceHelper.getColor(context, R.color.system_accent1_900),
+ primary0 = ColorResourceHelper.getColor(context, R.color.system_accent1_1000),
+
+ // The secondary tonal range from the generated dynamic color palette.
+ secondary100 = ColorResourceHelper.getColor(context, R.color.system_accent2_0),
+ secondary99 = ColorResourceHelper.getColor(context, R.color.system_accent2_10),
+ secondary95 = ColorResourceHelper.getColor(context, R.color.system_accent2_50),
+ secondary90 = ColorResourceHelper.getColor(context, R.color.system_accent2_100),
+ secondary80 = ColorResourceHelper.getColor(context, R.color.system_accent2_200),
+ secondary70 = ColorResourceHelper.getColor(context, R.color.system_accent2_300),
+ secondary60 = ColorResourceHelper.getColor(context, R.color.system_accent2_400),
+ secondary50 = ColorResourceHelper.getColor(context, R.color.system_accent2_500),
+ secondary40 = ColorResourceHelper.getColor(context, R.color.system_accent2_600),
+ secondary30 = ColorResourceHelper.getColor(context, R.color.system_accent2_700),
+ secondary20 = ColorResourceHelper.getColor(context, R.color.system_accent2_800),
+ secondary10 = ColorResourceHelper.getColor(context, R.color.system_accent2_900),
+ secondary0 = ColorResourceHelper.getColor(context, R.color.system_accent2_1000),
+
+ // The tertiary tonal range from the generated dynamic color palette.
+ tertiary100 = ColorResourceHelper.getColor(context, R.color.system_accent3_0),
+ tertiary99 = ColorResourceHelper.getColor(context, R.color.system_accent3_10),
+ tertiary95 = ColorResourceHelper.getColor(context, R.color.system_accent3_50),
+ tertiary90 = ColorResourceHelper.getColor(context, R.color.system_accent3_100),
+ tertiary80 = ColorResourceHelper.getColor(context, R.color.system_accent3_200),
+ tertiary70 = ColorResourceHelper.getColor(context, R.color.system_accent3_300),
+ tertiary60 = ColorResourceHelper.getColor(context, R.color.system_accent3_400),
+ tertiary50 = ColorResourceHelper.getColor(context, R.color.system_accent3_500),
+ tertiary40 = ColorResourceHelper.getColor(context, R.color.system_accent3_600),
+ tertiary30 = ColorResourceHelper.getColor(context, R.color.system_accent3_700),
+ tertiary20 = ColorResourceHelper.getColor(context, R.color.system_accent3_800),
+ tertiary10 = ColorResourceHelper.getColor(context, R.color.system_accent3_900),
+ tertiary0 = ColorResourceHelper.getColor(context, R.color.system_accent3_1000),
+ )
+
+private object ColorResourceHelper {
+ @DoNotInline
+ fun getColor(context: Context, @ColorRes id: Int): Color {
+ return Color(context.resources.getColor(id, context.theme))
+ }
+}
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/ContextCompat.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/ContextCompat.java
new file mode 100644
index 000000000..4ee23f7a0
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/ContextCompat.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.utils;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.modules.utils.build.SdkLevel;
+
+/**
+ * Helper Context compat class for {@link Context}.
+ */
+public class ContextCompat {
+ /**
+ * The default device ID, which is the ID of the primary (non-virtual) device.
+ *
+ * @see Context#DEVICE_ID_DEFAULT
+ */
+ public static final int DEVICE_ID_DEFAULT = 0;
+
+ private ContextCompat() {
+ }
+
+ /**
+ * @return The default device ID for pre V platforms, otherwise returns the device ID from
+ * the context.
+ */
+ public static int getDeviceId(@NonNull Context context) {
+ if (SdkLevel.isAtLeastU()) {
+ return context.getDeviceId();
+ } else {
+ return DEVICE_ID_DEFAULT;
+ }
+
+ }
+
+ /**
+ * Creates a new device context, if needed.
+ *
+ * @return A new context if the input context is for a different device, otherwise
+ * return the same context object. See {@link Context#DEVICE_ID_DEFAULT}
+ */
+ @NonNull
+ public static Context createDeviceContext(@NonNull Context context, int deviceId) {
+ if (SdkLevel.isAtLeastU()) {
+ return deviceId == context.getDeviceId()
+ ? context : context.createDeviceContext(deviceId);
+ } else {
+ if (deviceId != DEVICE_ID_DEFAULT) {
+ throw new IllegalArgumentException("Invalid device ID " + deviceId);
+ }
+ return context;
+ }
+ }
+}
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 f9345ef58..b822aa541 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/KotlinUtils.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("DEPRECATION")
+@file:Suppress("DEPRECATION", "LongLogTag")
package com.android.permissioncontroller.permission.utils
@@ -25,6 +25,8 @@ 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.annotation.SuppressLint
+import android.app.Activity
import android.app.ActivityManager
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
@@ -59,8 +61,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
@@ -74,6 +78,7 @@ import androidx.navigation.NavController
import androidx.preference.Preference
import androidx.preference.PreferenceGroup
import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.R
@@ -86,7 +91,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 +111,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 +137,21 @@ object KotlinUtils {
private val ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE =
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
- /** 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"
@@ -171,85 +168,43 @@ object KotlinUtils {
private const val PROPERTY_SAFETY_LABEL_CHANGES_JOB_SERVICE_KILL_SWITCH =
"safety_label_changes_job_service_kill_switch"
- /**
- * Whether the Permissions Hub 2 flag is enabled
- *
- * @return whether the flag is enabled
- */
- @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
- fun isPermissionsHub2FlagEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
- }
- /**
- * Whether to show the Permissions Dashboard
- *
- * @return whether to show the Permissions Dashboard.
- */
- @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
- fun shouldShowPermissionsDashboard(): Boolean {
- return isPermissionsHub2FlagEnabled()
- }
-
- /**
- * Whether the Camera and Mic Icons are enabled by flag.
- *
- * @return whether the Camera and Mic Icons are enabled.
- */
- @ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
- fun isCameraMicIconsFlagEnabled(): Boolean {
- return SdkLevel.isAtLeastS() && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_CAMERA_MIC_ICONS_ENABLED, true)
- }
+ data class Quadruple<out A, out B, out C, out D>(
+ val first: A,
+ val second: B,
+ val third: C,
+ val fourth: D
+ )
/**
- * 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.
*
* @return whether to show the icons.
*/
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun shouldShowCameraMicIndicators(): Boolean {
- return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled()
- }
-
- /**
- * Whether the location indicators are enabled by flag.
- *
- * @return whether the location indicators are enabled by flag.
- */
- @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_CAMERA_MIC_ICONS_ENABLED,
+ true
+ )
}
- /**
- * 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. */
@ChecksSdkIntAtLeast(Build.VERSION_CODES.S)
fun shouldShowLocationIndicators(): Boolean {
- return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled()
+ return SdkLevel.isAtLeastS() &&
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_LOCATION_INDICATORS_ENABLED,
+ false
+ )
}
- /**
- * 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)
- }
-
- /**
- * 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()
}
/**
@@ -259,8 +214,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 +229,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 +254,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 +267,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 +284,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 +298,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 +308,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 +342,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 +355,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 +380,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 +408,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 +424,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 +441,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 +462,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 +488,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 +501,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 {
@@ -519,19 +514,29 @@ object KotlinUtils {
}
/**
+ * Get the settings icon
+ *
+ * @param app The current application
+ * @param user The user for whom we want the icon
+ * @param pm The PackageManager
+ * @return Bitmap of the setting's icon, or null
+ */
+ fun getSettingsIcon(app: Application, user: UserHandle, pm: PackageManager): Bitmap? {
+ val settingsPackageName =
+ getPackageNameForIntent(pm, Settings.ACTION_SETTINGS)
+ ?: Constants.SETTINGS_PACKAGE_NAME_FALLBACK
+ return getBadgedPackageIconBitmap(app, user, settingsPackageName)
+ }
+
+ /**
* Gets a package's badged icon from the system.
*
* @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)
@@ -542,12 +547,35 @@ object KotlinUtils {
}
/**
+ * Get the icon of a package
+ *
+ * @param application The current application
+ * @param user The user for whom we want the icon
+ * @param packageName The name of the package whose icon we want
+ * @return Bitmap of the package icon, or null
+ */
+ fun getBadgedPackageIconBitmap(
+ application: Application,
+ user: UserHandle,
+ packageName: String
+ ): Bitmap? {
+ val drawable = getBadgedPackageIcon(application, packageName, user)
+
+ val icon =
+ if (drawable != null) {
+ convertToBitmap(drawable)
+ } else {
+ null
+ }
+ return icon
+ }
+
+ /**
* Gets a package's badged label from the system.
*
* @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 +589,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)
@@ -571,19 +603,31 @@ object KotlinUtils {
}
/**
+ * Returns the name of the package that resolves the specified intent action
+ *
+ * @param pm The PackageManager
+ * @param intentAction The name of the intent action
+ * @return The package's name, or null
+ */
+ fun getPackageNameForIntent(pm: PackageManager, intentAction: String): String? {
+ val intent = Intent(intentAction)
+ return intent.resolveActivity(pm)?.packageName
+ }
+
+ /**
* Gets a package's uid, using a cached liveData value, if the liveData is currently being
* observed (and thus has an up-to-date value).
*
* @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 +638,32 @@ object KotlinUtils {
}
}
- /**
- * Return a specific MIME type, if a set of permissions is associated with one
- */
+ @Suppress("MissingPermission")
+ 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,36 +681,36 @@ 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,
- permName: String,
- targetSdk: Int
- ): Boolean {
+ @JvmStatic
+ fun isPermissionSplitFromNonRuntime(app: Context, permName: String, targetSdk: Int): Boolean {
val permissionManager = app.getSystemService(PermissionManager::class.java) ?: return false
val splitPerms = permissionManager.splitPermissions
val size = splitPerms.size
@@ -664,8 +731,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(
@@ -683,6 +749,10 @@ object KotlinUtils {
}
}
+ val deviceId = group.deviceId
+ // Create a new context with the given deviceId so that permission updates will be bound
+ // to the device
+ val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId)
val newPerms = mutableMapOf<String, LightPermission>()
for ((permName, perm) in group.permissions) {
if (permName !in filterPermissions) {
@@ -690,14 +760,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)
+ context.packageManager.updatePermissionFlags(
+ permName,
+ group.packageName,
+ group.userHandle,
+ *flags
+ )
}
- newPerms[permName] = LightPermission(group.packageInfo, perm.permInfo,
- perm.isGrantedIncludingAppOp, perm.flags or flagsToSet, perm.foregroundPerms)
+ newPerms[permName] =
+ LightPermission(
+ group.packageInfo,
+ perm.permInfo,
+ perm.isGrantedIncludingAppOp,
+ 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,22 +793,27 @@ 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
fun grantForegroundRuntimePermissions(
app: Application,
group: LightAppPermGroup,
- filterPermissions: List<String> = group.permissions.keys.toList(),
+ filterPermissions: Collection<String> = group.permissions.keys,
isOneTime: Boolean = false,
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,21 +824,27 @@ 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
fun grantBackgroundRuntimePermissions(
app: Application,
group: LightAppPermGroup,
- filterPermissions: List<String> = group.permissions.keys.toList()
+ filterPermissions: Collection<String> = group.permissions.keys
): LightAppPermGroup {
- return grantRuntimePermissions(app, group, true, false, false, false,
- filterPermissions)
+ return grantRuntimePermissions(
+ app,
+ group,
+ grantBackground = true,
+ isOneTime = false,
+ userFixed = false,
+ withoutAppOps = false,
+ filterPermissions = filterPermissions
+ )
}
+ @SuppressLint("MissingPermission")
private fun grantRuntimePermissions(
app: Application,
group: LightAppPermGroup,
@@ -756,52 +852,78 @@ object KotlinUtils {
isOneTime: Boolean = false,
userFixed: Boolean = false,
withoutAppOps: Boolean = false,
- filterPermissions: List<String> = group.permissions.keys.toList(),
+ filterPermissions: Collection<String> = group.permissions.keys
): LightAppPermGroup {
+ val deviceId = group.deviceId
val newPerms = group.permissions.toMutableMap()
var shouldKillForAnyPermission = false
for (permName in filterPermissions) {
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
}
}
+
+ // Create a new context with the given deviceId so that permission updates will be bound
+ // to the device
+ val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId)
+
if (!newPerms.isEmpty()) {
val user = UserHandle.getUserHandleForUid(group.packageInfo.uid)
for (groupPerm in group.allPermissions.values) {
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)
+ context.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.isGrantedIncludingAppOp }) {
if (SdkLevel.isAtLeastT()) {
- app.getSystemService(PermissionManager::class.java)!!.startOneTimePermissionSession(
- group.packageName, Utils.getOneTimePermissionsTimeout(),
+ context
+ .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)
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE
+ )
} else {
- app.getSystemService(PermissionManager::class.java)!!.startOneTimePermissionSession(
- group.packageName, Utils.getOneTimePermissionsTimeout(),
+ context
+ .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)
+ ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE
+ )
}
}
return newGroup
@@ -813,15 +935,15 @@ 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>
*/
+ @Suppress("MissingPermission")
private fun grantRuntimePermission(
app: Application,
perm: LightPermission,
@@ -832,9 +954,11 @@ object KotlinUtils {
): Pair<LightPermission, Boolean> {
val pkgInfo = group.packageInfo
val user = UserHandle.getUserHandleForUid(pkgInfo.uid)
+ val deviceId = group.deviceId
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
@@ -845,6 +969,10 @@ object KotlinUtils {
var isGranted = perm.isGrantedIncludingAppOp
var shouldKill = false
+ // Create a new context with the given deviceId so that permission updates will be bound
+ // to the device
+ val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId)
+
// Grant the permission if needed.
if (!perm.isGrantedIncludingAppOp) {
val affectsAppOp = permissionToOp(perm.name) != null || perm.isBackgroundPermission
@@ -855,11 +983,17 @@ 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)
+ context.packageManager.updatePermissionFlags(
+ perm.name,
+ group.packageName,
+ PERMISSION_CONTROLLER_CHANGED_FLAG_MASK,
+ oldFlags,
+ user
+ )
+ // TODO: Update this method once AppOp is device aware
disallowAppOp(app, perm, group)
}
- app.packageManager.grantRuntimePermission(group.packageName, perm.name, user)
+ context.packageManager.grantRuntimePermission(group.packageName, perm.name, user)
isGranted = true
} else if (affectsAppOp) {
// Legacy apps do not know that they have to retry access to a
@@ -869,16 +1003,18 @@ 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
// before the permission grant.
if (affectsAppOp && !withoutAppOps) {
+ // TODO: Update this method once AppOp is device aware
allowAppOp(app, perm, group)
}
}
@@ -898,11 +1034,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 +1059,18 @@ object KotlinUtils {
}
if (oldFlags != newFlags) {
- app.packageManager.updatePermissionFlags(perm.name, group.packageInfo.packageName,
- PERMISSION_CONTROLLER_CHANGED_FLAG_MASK, newFlags, user)
+ context.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 +1083,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
@@ -953,10 +1093,17 @@ object KotlinUtils {
userFixed: Boolean = false,
oneTime: Boolean = false,
forceRemoveRevokedCompat: Boolean = false,
- filterPermissions: List<String> = group.permissions.keys.toList()
+ filterPermissions: Collection<String> = group.permissions.keys
): LightAppPermGroup {
- return revokeRuntimePermissions(app, group, false, userFixed, oneTime,
- forceRemoveRevokedCompat, filterPermissions)
+ return revokeRuntimePermissions(
+ app,
+ group,
+ false,
+ userFixed,
+ oneTime,
+ forceRemoveRevokedCompat,
+ filterPermissions
+ )
}
/**
@@ -968,9 +1115,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
@@ -980,12 +1125,20 @@ object KotlinUtils {
userFixed: Boolean = false,
oneTime: Boolean = false,
forceRemoveRevokedCompat: Boolean = false,
- filterPermissions: List<String> = group.permissions.keys.toList()
+ filterPermissions: Collection<String> = group.permissions.keys
): LightAppPermGroup {
- return revokeRuntimePermissions(app, group, true, userFixed, oneTime,
- forceRemoveRevokedCompat, filterPermissions)
+ return revokeRuntimePermissions(
+ app,
+ group,
+ true,
+ userFixed,
+ oneTime,
+ forceRemoveRevokedCompat,
+ filterPermissions
+ )
}
+ @Suppress("MissingPermission")
private fun revokeRuntimePermissions(
app: Application,
group: LightAppPermGroup,
@@ -993,8 +1146,9 @@ object KotlinUtils {
userFixed: Boolean,
oneTime: Boolean,
forceRemoveRevokedCompat: Boolean = false,
- filterPermissions: List<String>
+ filterPermissions: Collection<String>
): LightAppPermGroup {
+ val deviceId = group.deviceId
val wasOneTime = group.isOneTime
val newPerms = group.permissions.toMutableMap()
var shouldKillForAnyPermission = false
@@ -1003,8 +1157,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 +1172,27 @@ 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)
+ // Create a new context with the given deviceId so that permission updates will be bound
+ // to the device
+ val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId)
+ context
+ .getSystemService(PermissionManager::class.java)!!
+ .stopOneTimePermissionSession(group.packageName)
}
return newGroup
}
@@ -1042,8 +1214,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,9 +1232,9 @@ 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
*/
+ @Suppress("MissingPermission")
private fun anyPermsOfPackageOneTimeGranted(
app: Application,
packageInfo: LightPackageInfo,
@@ -1075,17 +1248,19 @@ 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
}
}
return false
}
+
/**
* Revokes a single runtime permission.
*
@@ -1093,11 +1268,11 @@ 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>
*/
+ @Suppress("MissingPermission")
private fun revokeRuntimePermission(
app: Application,
perm: LightPermission,
@@ -1113,18 +1288,32 @@ object KotlinUtils {
val user = UserHandle.getUserHandleForUid(group.packageInfo.uid)
var newFlags = perm.flags
+ val deviceId = group.deviceId
var isGranted = perm.isGrantedIncludingAppOp
val supportsRuntime = group.packageInfo.targetSdkVersion >= Build.VERSION_CODES.M
var shouldKill = false
val affectsAppOp = permissionToOp(perm.name) != null || perm.isBackgroundPermission
+ // Create a new context with the given deviceId so that permission updates will be bound
+ // to the device
+ val context = ContextCompat.createDeviceContext(app.applicationContext, deviceId)
+
if (perm.isGrantedIncludingAppOp || (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)
+ context.packageManager.revokeRuntimePermission(
+ group.packageInfo.packageName,
+ perm.name,
+ user
+ )
isGranted = false
if (forceRemoveRevokedCompat) {
newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKED_COMPAT)
@@ -1143,24 +1332,33 @@ object KotlinUtils {
newFlags = newFlags.clearFlag(PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
if (affectsAppOp) {
+ // TODO: Update this method once AppOp is device aware
disallowAppOp(app, perm, group)
}
}
// 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)
+ context.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 +1374,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 +1399,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 +1431,29 @@ object KotlinUtils {
val appOpName = permissionToOp(foregroundPermName) ?: continue
if (fgPerm != null && fgPerm.isGrantedIncludingAppOp) {
- 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.isGrantedIncludingAppOp) 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.isGrantedIncludingAppOp) 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 +1464,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 +1492,9 @@ object KotlinUtils {
val fgPerm = group.permissions[foregroundPermName]
if (fgPerm != null && fgPerm.isGrantedIncludingAppOp) {
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 +1512,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(
@@ -1334,7 +1525,7 @@ object KotlinUtils {
if (currentMode == mode) {
return false
}
- manager.setUidMode(op, uid, mode)
+ @Suppress("MissingPermission") manager.setUidMode(op, uid, mode)
return true
}
@@ -1343,22 +1534,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,23 +1561,31 @@ 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
- isInSetup || isInDeferredSetup
+ val isInSetup = getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userContext, user) == 0
+ if (isInSetup) return true
+
+ val isInDeferredSetup =
+ getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userContext, user) ==
+ Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+ return isInDeferredSetup
+ }
+
+ @SuppressLint("LongLogTag")
+ private fun getSecureInt(settingName: String, userContext: Context, user: UserHandle): Int? =
+ try {
+ Settings.Secure.getInt(userContext.contentResolver, settingName, user.identifier)
} catch (e: Settings.SettingNotFoundException) {
- Log.w(LOG_TAG, "Failed to check if the user is in restore: $e")
- false
+ Log.i(LOG_TAG, "Setting $settingName not found", e)
+ null
}
- }
/**
* Determine if a given package has a launch intent. Will function correctly even if called
@@ -1391,22 +1593,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 +1631,58 @@ 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)
+ )
+ val fineIsOneTime =
+ group.permissions[Manifest.permission.ACCESS_FINE_LOCATION]?.isOneTime ?: false
+ setGroupFlags(
+ app,
+ group,
PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY to false,
- filterPermissions = listOf(Manifest.permission.ACCESS_COARSE_LOCATION))
+ PackageManager.FLAG_PERMISSION_ONE_TIME to fineIsOneTime,
+ 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 +1706,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 +1737,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 +1749,44 @@ 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 }
-): T {
- return if (isInitialized()) {
- value!!
+ observe: LD.(Observer<T?>) -> Unit = { observeForever(it) },
+ isValueInitialized: LD.() -> Boolean = { value != null }
+): T? {
+ return if (isValueInitialized()) {
+ value
} 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)
+ suspendCoroutine { continuation: Continuation<T?> ->
+ val observer = AtomicReference<Observer<T?>>()
+ 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 +1794,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 +1817,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/LocationUtils.java b/PermissionController/src/com/android/permissioncontroller/permission/utils/LocationUtils.java
index da4895304..3d1e44b6a 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/LocationUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/LocationUtils.java
@@ -15,6 +15,8 @@
*/
package com.android.permissioncontroller.permission.utils;
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
+import static android.location.LocationManager.EXTRA_ADAS_GNSS_ENABLED;
import static android.location.LocationManager.EXTRA_LOCATION_ENABLED;
import android.Manifest;
@@ -27,6 +29,7 @@ import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -34,11 +37,16 @@ import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.flags.Flags;
import com.android.permissioncontroller.PermissionControllerApplication;
import com.android.permissioncontroller.R;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
public class LocationUtils {
@@ -84,6 +92,35 @@ public class LocationUtils {
return context.getSystemService(LocationManager.class).isLocationEnabled();
}
+ /** Checks if the automotive location bypass is enabled. */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static boolean isAutomotiveLocationBypassEnabled(Context context) {
+ return context.getSystemService(LocationManager.class).isAdasGnssLocationEnabled();
+ }
+
+ /** Return the automotive location bypass allowlist. */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static Collection<String> getAutomotiveLocationBypassAllowlist(Context context) {
+ // TODO(b/335763768): Remove reflection once getAdasAllowlist() is a System API
+ try {
+ LocationManager locationManager = context.getSystemService(LocationManager.class);
+ Object packageTagsList =
+ LocationManager.class.getMethod("getAdasAllowlist").invoke(locationManager);
+ return (Collection<String>) packageTagsList.getClass().getMethod("getPackages")
+ .invoke(packageTagsList);
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot get location bypass allowlist: " + e);
+ return new ArrayList<String>();
+ }
+ }
+
+ /** Checks if the provided package is an automotive location bypass allowlisted package. */
+ public static boolean isAutomotiveLocationBypassAllowlistedPackage(
+ Context context, String packageName) {
+ return SdkLevel.isAtLeastV() && Flags.addBannersToPrivacySensitiveAppsForAaos()
+ && getAutomotiveLocationBypassAllowlist(context).contains(packageName);
+ }
+
/** Checks if the provided package is a location provider. */
public static boolean isLocationProvider(Context context, String packageName) {
return context.getSystemService(LocationManager.class).isProviderPackage(packageName);
@@ -126,52 +163,105 @@ public class LocationUtils {
void onLocationStateChange(boolean enabled);
}
- private static final ArrayList<LocationListener> sLocationListeners = new ArrayList<>();
-
- private static BroadcastReceiver sLocationBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- boolean isEnabled = intent.getBooleanExtra(EXTRA_LOCATION_ENABLED, true);
- sMainHandler.postDelayed(() -> {
- synchronized (sLocationListeners) {
- for (LocationListener l : sLocationListeners) {
- l.onLocationStateChange(isEnabled);
- }
- }
- }, LOCATION_UPDATE_DELAY_MS);
- }
- };
+ /**
+ * Add a location listener, which will be notified if the automotive location bypass state is
+ * enabled or disabled.
+ * @param listener the listener to add
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static void addAutomotiveLocationBypassListener(LocationListener listener) {
+ addLocationListener(listener, sAutomotiveLocationBypassListeners,
+ sAutomotiveLocationBypassBroadcastReceiver,
+ LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED);
+ }
/**
- * Add a LocationListener, which will be notified if the location provider is enabled or
- * disabled
+ * Add a location listener, which will be notified if the main location state is enabled or
+ * disabled.
* @param listener the listener to add
*/
public static void addLocationListener(LocationListener listener) {
- synchronized (sLocationListeners) {
- boolean wasEmpty = sLocationListeners.isEmpty();
- sLocationListeners.add(listener);
- if (wasEmpty) {
- IntentFilter intentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
- PermissionControllerApplication.get().getApplicationContext()
- .registerReceiverForAllUsers(sLocationBroadcastReceiver, intentFilter,
- null, null);
- }
- }
+ addLocationListener(listener, sLocationListeners, sLocationBroadcastReceiver,
+ LocationManager.MODE_CHANGED_ACTION);
}
/**
- * Remove a LocationListener
+ * Remove an automotive location bypass listener
+ * @param listener The listener to remove
+ *
+ * @return True if it was successfully removed, false otherwise
+ */
+ public static boolean removeAutomotiveLocationBypassListener(LocationListener listener) {
+ return removeLocationListener(listener, sAutomotiveLocationBypassListeners,
+ sAutomotiveLocationBypassBroadcastReceiver);
+ }
+
+ /**
+ * Remove a main location listener
* @param listener The listener to remove
*
* @return True if it was successfully removed, false otherwise
*/
public static boolean removeLocationListener(LocationListener listener) {
- synchronized (sLocationListeners) {
- boolean success = sLocationListeners.remove(listener);
- if (success && sLocationListeners.isEmpty()) {
+ return removeLocationListener(listener, sLocationListeners, sLocationBroadcastReceiver);
+ }
+
+ private static final List<LocationListener> sAutomotiveLocationBypassListeners =
+ new ArrayList<>();
+ private static final List<LocationListener> sLocationListeners = new ArrayList<>();
+
+ private static final BroadcastReceiver sAutomotiveLocationBypassBroadcastReceiver =
+ getLocationBroadcastReceiver(
+ SdkLevel.isAtLeastT() ? EXTRA_ADAS_GNSS_ENABLED : EXTRA_LOCATION_ENABLED,
+ sAutomotiveLocationBypassListeners);
+ private static final BroadcastReceiver sLocationBroadcastReceiver =
+ getLocationBroadcastReceiver(EXTRA_LOCATION_ENABLED, sLocationListeners);
+
+ private static BroadcastReceiver getLocationBroadcastReceiver(String locationIntentExtra,
+ List<LocationListener> locationListeners) {
+ return new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean isEnabled = intent.getBooleanExtra(locationIntentExtra, true);
+ sMainHandler.postDelayed(() -> {
+ synchronized (locationListeners) {
+ for (LocationListener l : locationListeners) {
+ l.onLocationStateChange(isEnabled);
+ }
+ }
+ }, LOCATION_UPDATE_DELAY_MS);
+ }
+ };
+ }
+
+ private static void addLocationListener(LocationListener listener,
+ List<LocationListener> locationListeners, BroadcastReceiver locationBroadcastReceiver,
+ String intentAction) {
+ synchronized (locationListeners) {
+ boolean wasEmpty = locationListeners.isEmpty();
+ locationListeners.add(listener);
+ if (wasEmpty) {
+ IntentFilter intentFilter = new IntentFilter(intentAction);
+ if (SdkLevel.isAtLeastU()) {
+ PermissionControllerApplication.get().getApplicationContext()
+ .registerReceiverForAllUsers(locationBroadcastReceiver, intentFilter,
+ null, null, RECEIVER_NOT_EXPORTED);
+ } else {
+ PermissionControllerApplication.get().getApplicationContext()
+ .registerReceiverForAllUsers(locationBroadcastReceiver, intentFilter,
+ null, null);
+ }
+ }
+ }
+ }
+
+ private static boolean removeLocationListener(LocationListener listener,
+ List<LocationListener> locationListeners, BroadcastReceiver locationBroadcastReceiver) {
+ synchronized (locationListeners) {
+ boolean success = locationListeners.remove(listener);
+ if (success && locationListeners.isEmpty()) {
PermissionControllerApplication.get().getApplicationContext()
- .unregisterReceiver(sLocationBroadcastReceiver);
+ .unregisterReceiver(locationBroadcastReceiver);
}
return success;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
index b06a09b28..3198a4c09 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/PermissionMapping.kt
@@ -22,11 +22,11 @@ import android.app.AppOpsManager
import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.health.connect.HealthPermissions.HEALTH_PERMISSION_GROUP
+import android.permission.flags.Flags
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
@@ -34,17 +34,18 @@ import com.android.permission.safetylabel.DataCategoryConstants
*/
object PermissionMapping {
- private val LOG_TAG = "PermissionMapping"
+ private const 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 +54,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 +70,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 +203,6 @@ object PermissionMapping {
* platform permission.
*
* @param permission the permission to resolve
- *
* @return The group the permission belongs to
*/
@JvmStatic
@@ -214,7 +214,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 +229,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 +243,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 +298,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 +307,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 +331,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 +346,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)
@@ -375,15 +375,27 @@ object PermissionMapping {
if (opName == AppOpsManager.OPSTR_PHONE_CALL_CAMERA) {
return Manifest.permission_group.CAMERA
}
+ if (
+ SdkLevel.isAtLeastV() &&
+ Flags.locationBypassPrivacyDashboardEnabled() &&
+ opName == AppOpsManager.OPSTR_EMERGENCY_LOCATION
+ ) {
+ return Manifest.permission_group.LOCATION
+ }
- return AppOpsManager.opToPermission(opName)?.let { getGroupOfPlatformPermission(it) }
+ return try {
+ AppOpsManager.opToPermission(opName)?.let { getGroupOfPlatformPermission(it) }
+ } catch (e: IllegalArgumentException) {
+ Log.wtf(LOG_TAG, "No permission group found for $opName")
+ null
+ }
}
/**
* 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..e5de63f32 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;
@@ -53,6 +54,7 @@ import android.Manifest;
import android.app.AppOpsManager;
import android.app.Application;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -67,17 +69,20 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
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;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.Html;
@@ -109,6 +114,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 +127,6 @@ import java.util.Locale;
import java.util.Random;
import java.util.Set;
-import kotlin.Triple;
-
public final class Utils {
@Retention(SOURCE)
@@ -160,9 +165,6 @@ public final class Utils {
public static final String PROPERTY_SYSTEM_EXEMPT_HIBERNATION_ENABLED =
"system_exempt_hibernation_enabled";
- /** Whether to show the Permissions Hub. */
- private static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
-
/** The timeout for one-time permissions */
private static final String PROPERTY_ONE_TIME_PERMISSIONS_TIMEOUT_MILLIS =
"one_time_permissions_timeout_millis";
@@ -171,10 +173,6 @@ public final class Utils {
private static final String PROPERTY_ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS =
"one_time_permissions_killed_delay_millis";
- /** Whether to show location access check notifications. */
- private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED =
- "location_access_check_enabled";
-
/** Whether to show health permission in various permission controller UIs. */
private static final String PROPERTY_HEALTH_PERMISSION_UI_ENABLED =
"health_permission_ui_enabled";
@@ -195,9 +193,6 @@ public final class Utils {
public static final String PROPERTY_PERMISSION_DECISIONS_MAX_DATA_AGE_MILLIS =
"permission_decisions_max_data_age_millis";
- /** Whether or not warning banner is displayed when device sensors are off **/
- public static final String PROPERTY_WARNING_BANNER_DISPLAY_ENABLED = "warning_banner_enabled";
-
/** All permission whitelists. */
public static final int FLAGS_PERMISSION_WHITELIST_ALL =
PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
@@ -222,10 +217,13 @@ public final class Utils {
public static final long ONE_TIME_PERMISSIONS_KILLED_DELAY_MILLIS = 5 * 1000;
private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_RES;
+ private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_DEVICE_AWARE_RES;
private static final ArrayMap<String, Integer> PERM_GROUP_REQUEST_DETAIL_RES;
private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_RES;
+ private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES;
private static final ArrayMap<String, Integer> PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES;
private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_RES;
+ private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES;
private static final ArrayMap<String, Integer> PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES;
/** Permission -> Sensor codes */
@@ -234,6 +232,8 @@ public final class Utils {
private static final ArrayMap<String, Integer> PERM_BLOCKED_ICON;
/** Permission -> Title res id */
private static final ArrayMap<String, Integer> PERM_BLOCKED_TITLE;
+ /** Permission -> Title res id */
+ private static final ArrayMap<String, Integer> PERM_BLOCKED_TITLE_AUTOMOTIVE;
public static final int FLAGS_ALWAYS_USER_SENSITIVE =
FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
@@ -282,6 +282,38 @@ public final class Utils {
PERM_GROUP_REQUEST_RES.put(SENSORS, R.string.permgrouprequest_sensors);
PERM_GROUP_REQUEST_RES.put(NOTIFICATIONS, R.string.permgrouprequest_notifications);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES = new ArrayMap<>();
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(CONTACTS,
+ R.string.permgrouprequest_device_aware_contacts);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(LOCATION,
+ R.string.permgrouprequest_device_aware_location);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(NEARBY_DEVICES,
+ R.string.permgrouprequest_device_aware_nearby_devices);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(CALENDAR,
+ R.string.permgrouprequest_device_aware_calendar);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(SMS, R.string.permgrouprequest_device_aware_sms);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(STORAGE,
+ R.string.permgrouprequest_device_aware_storage);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(READ_MEDIA_AURAL,
+ R.string.permgrouprequest_device_aware_read_media_aural);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(READ_MEDIA_VISUAL,
+ R.string.permgrouprequest_device_aware_read_media_visual);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(MICROPHONE,
+ R.string.permgrouprequest_device_aware_microphone);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES
+ .put(ACTIVITY_RECOGNITION,
+ R.string.permgrouprequest_device_aware_activityRecognition);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(CAMERA,
+ R.string.permgrouprequest_device_aware_camera);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(CALL_LOG,
+ R.string.permgrouprequest_device_aware_calllog);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(PHONE,
+ R.string.permgrouprequest_device_aware_phone);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(SENSORS,
+ R.string.permgrouprequest_device_aware_sensors);
+ PERM_GROUP_REQUEST_DEVICE_AWARE_RES.put(NOTIFICATIONS,
+ R.string.permgrouprequest_device_aware_notifications);
+
PERM_GROUP_REQUEST_DETAIL_RES = new ArrayMap<>();
PERM_GROUP_REQUEST_DETAIL_RES.put(LOCATION, R.string.permgrouprequestdetail_location);
PERM_GROUP_REQUEST_DETAIL_RES.put(MICROPHONE, R.string.permgrouprequestdetail_microphone);
@@ -297,6 +329,16 @@ public final class Utils {
PERM_GROUP_BACKGROUND_REQUEST_RES
.put(SENSORS, R.string.permgroupbackgroundrequest_sensors);
+ PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES = new ArrayMap<>();
+ PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES
+ .put(LOCATION, R.string.permgroupbackgroundrequest_device_aware_location);
+ PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES
+ .put(MICROPHONE, R.string.permgroupbackgroundrequest_device_aware_microphone);
+ PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES
+ .put(CAMERA, R.string.permgroupbackgroundrequest_device_aware_camera);
+ PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES
+ .put(SENSORS, R.string.permgroupbackgroundrequest_device_aware_sensors);
+
PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES = new ArrayMap<>();
PERM_GROUP_BACKGROUND_REQUEST_DETAIL_RES
.put(LOCATION, R.string.permgroupbackgroundrequestdetail_location);
@@ -313,6 +355,16 @@ public final class Utils {
PERM_GROUP_UPGRADE_REQUEST_RES.put(CAMERA, R.string.permgroupupgraderequest_camera);
PERM_GROUP_UPGRADE_REQUEST_RES.put(SENSORS, R.string.permgroupupgraderequest_sensors);
+ PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES = new ArrayMap<>();
+ PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES.put(LOCATION,
+ R.string.permgroupupgraderequest_device_aware_location);
+ PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES.put(MICROPHONE,
+ R.string.permgroupupgraderequest_device_aware_microphone);
+ PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES.put(CAMERA,
+ R.string.permgroupupgraderequest_device_aware_camera);
+ PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES.put(SENSORS,
+ R.string.permgroupupgraderequest_device_aware_sensors);
+
PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES = new ArrayMap<>();
PERM_GROUP_UPGRADE_REQUEST_DETAIL_RES
.put(LOCATION, R.string.permgroupupgraderequestdetail_location);
@@ -339,12 +391,18 @@ public final class Utils {
PERM_BLOCKED_TITLE.put(MICROPHONE, R.string.blocked_microphone_title);
PERM_BLOCKED_TITLE.put(LOCATION, R.string.blocked_location_title);
+ PERM_BLOCKED_TITLE_AUTOMOTIVE = new ArrayMap<>();
+ PERM_BLOCKED_TITLE_AUTOMOTIVE.put(CAMERA, R.string.automotive_blocked_camera_title);
+ PERM_BLOCKED_TITLE_AUTOMOTIVE.put(MICROPHONE, R.string.automotive_blocked_microphone_title);
+ PERM_BLOCKED_TITLE_AUTOMOTIVE.put(LOCATION, R.string.automotive_blocked_location_title);
}
private Utils() {
/* do nothing - hide constructor */
}
+ private static Object sLock = new Object();
+
private static ArrayMap<UserHandle, Context> sUserContexts = new ArrayMap<>();
/**
@@ -359,11 +417,13 @@ public final class Utils {
* @throws RuntimeException If the app has no package name attached, which should never happen
*/
public static @NonNull Context getUserContext(Context context, UserHandle user) {
- if (!sUserContexts.containsKey(user)) {
- sUserContexts.put(user, context.getApplicationContext()
- .createContextAsUser(user, 0));
+ synchronized (sLock) {
+ if (!sUserContexts.containsKey(user)) {
+ sUserContexts.put(user, context.getApplicationContext()
+ .createContextAsUser(user, 0));
+ }
+ return Preconditions.checkNotNull(sUserContexts.get(user));
}
- return Preconditions.checkNotNull(sUserContexts.get(user));
}
/**
@@ -698,11 +758,16 @@ public final class Utils {
* @param groupName The name of the permission group
* @param context A context to resolve resources
* @param requestRes The resource id of the grant request message
- *
* @return The formatted message to be used as title when granting permissions
*/
- public static CharSequence getRequestMessage(CharSequence appLabel, String packageName,
- String groupName, Context context, @StringRes int requestRes) {
+ @NonNull
+ public static CharSequence getRequestMessage(
+ @NonNull String appLabel,
+ @NonNull String packageName,
+ @NonNull String groupName,
+ @NonNull Context context,
+ @StringRes int requestRes) {
+ String escapedAppLabel = Html.escapeHtml(appLabel);
boolean isIsolatedStorage;
try {
@@ -712,15 +777,76 @@ public final class Utils {
}
if (groupName.equals(STORAGE) && isIsolatedStorage) {
return Html.fromHtml(
- String.format(context.getResources().getConfiguration().getLocales().get(0),
+ String.format(
+ context.getResources().getConfiguration().getLocales().get(0),
context.getString(R.string.permgrouprequest_storage_isolated),
- appLabel), 0);
+ escapedAppLabel),
+ 0);
+ } else if (requestRes != 0) {
+ return Html.fromHtml(context.getResources().getString(requestRes, escapedAppLabel), 0);
+ }
+
+ return Html.fromHtml(
+ context.getString(
+ R.string.permission_warning_template,
+ escapedAppLabel,
+ loadGroupDescription(context, groupName, context.getPackageManager())),
+ 0);
+ }
+
+ /**
+ * Get the message shown to grant a permission group to an app.
+ *
+ * @param appLabel The label of the app
+ * @param packageName The package name of the app
+ * @param groupName The name of the permission group
+ * @param context A context to resolve resources
+ * @param requestRes The resource id of the grant request message
+ * @return The formatted message to be used as title when granting permissions
+ */
+ @NonNull
+ public static CharSequence getRequestMessage(
+ @NonNull String appLabel,
+ @NonNull String packageName,
+ @NonNull String groupName,
+ @NonNull String deviceLabel,
+ @NonNull Context context,
+ Boolean isDeviceAwareMessage,
+ @StringRes int requestRes) {
+ if (!isDeviceAwareMessage) {
+ return getRequestMessage(appLabel, packageName, groupName, context, requestRes);
+ }
+ String escapedAppLabel = Html.escapeHtml(appLabel);
+
+ boolean isIsolatedStorage;
+ try {
+ isIsolatedStorage = !isNonIsolatedStorage(context, packageName);
+ } catch (NameNotFoundException e) {
+ isIsolatedStorage = false;
+ }
+ if (groupName.equals(STORAGE) && isIsolatedStorage) {
+ String escapedDeviceLabel = Html.escapeHtml(deviceLabel);
+ return Html.fromHtml(
+ String.format(
+ context.getResources().getConfiguration().getLocales().get(0),
+ context.getString(
+ R.string.permgrouprequest_device_aware_storage_isolated),
+ escapedAppLabel,
+ escapedDeviceLabel),
+ 0);
+
} else if (requestRes != 0) {
- return Html.fromHtml(context.getResources().getString(requestRes, appLabel), 0);
+ String escapedDeviceLabel = Html.escapeHtml(deviceLabel);
+ return Html.fromHtml(context.getResources().getString(requestRes, escapedAppLabel,
+ escapedDeviceLabel), 0);
}
- return Html.fromHtml(context.getString(R.string.permission_warning_template, appLabel,
- loadGroupDescription(context, groupName, context.getPackageManager())), 0);
+ return Html.fromHtml(
+ context.getString(
+ R.string.permission_warning_template,
+ escapedAppLabel,
+ loadGroupDescription(context, groupName, context.getPackageManager())),
+ 0);
}
private static CharSequence loadGroupDescription(Context context, String groupName,
@@ -838,8 +964,9 @@ public final class Utils {
if (context.getPackageManager().resolveActivity(intent, 0) == null) {
return;
}
- MenuItem searchItem = menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.search_menu);
- searchItem.setIcon(R.drawable.ic_search_24dp);
+ MenuItem searchItem = menu.add(Menu.NONE, Menu.NONE, Menu.NONE,
+ com.android.settingslib.search.widget.R.string.search_menu);
+ searchItem.setIcon(com.android.settingslib.search.widget.R.drawable.ic_search_24dp);
searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
searchItem.setOnMenuItemClickListener(item -> {
try {
@@ -917,16 +1044,6 @@ public final class Utils {
}
/**
- * Whether the Location Access Check is enabled.
- *
- * @return {@code true} iff the Location Access Check is enabled.
- */
- public static boolean isLocationAccessCheckEnabled() {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_ACCESS_CHECK_ENABLED, true);
- }
-
- /**
* Whether we should show health permissions as platform permissions in the various
* permission controller UI.
*/
@@ -943,6 +1060,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
@@ -1013,7 +1194,21 @@ public final class Utils {
* @return The id or 0 if the permission group doesn't exist or have a message
*/
public static int getRequest(String groupName) {
- return PERM_GROUP_REQUEST_RES.getOrDefault(groupName, 0);
+ return getRequest(groupName, false);
+ }
+
+ /**
+ * The resource id for the request message for a permission group for a specific device
+ *
+ * @param groupName Permission group name
+ * @return The id or 0 if the permission group doesn't exist or have a message
+ */
+ public static int getRequest(String groupName, Boolean isDeviceAwareMessage) {
+ if (isDeviceAwareMessage) {
+ return PERM_GROUP_REQUEST_DEVICE_AWARE_RES.getOrDefault(groupName, 0);
+ } else {
+ return PERM_GROUP_REQUEST_RES.getOrDefault(groupName, 0);
+ }
}
/**
@@ -1031,7 +1226,22 @@ public final class Utils {
* @return The id or 0 if the permission group doesn't exist or have a message
*/
public static int getBackgroundRequest(String groupName) {
- return PERM_GROUP_BACKGROUND_REQUEST_RES.getOrDefault(groupName, 0);
+ return getBackgroundRequest(groupName, false);
+ }
+
+ /**
+ * The resource id for the background request message for a permission group for a specific
+ * device
+ *
+ * @param groupName Permission group name
+ * @return The id or 0 if the permission group doesn't exist or have a message
+ */
+ public static int getBackgroundRequest(String groupName, Boolean isDeviceAwareMessage) {
+ if (isDeviceAwareMessage) {
+ return PERM_GROUP_BACKGROUND_REQUEST_DEVICE_AWARE_RES.getOrDefault(groupName, 0);
+ } else {
+ return PERM_GROUP_BACKGROUND_REQUEST_RES.getOrDefault(groupName, 0);
+ }
}
/**
@@ -1049,7 +1259,21 @@ public final class Utils {
* @return The id or 0 if the permission group doesn't exist or have a message
*/
public static int getUpgradeRequest(String groupName) {
- return PERM_GROUP_UPGRADE_REQUEST_RES.getOrDefault(groupName, 0);
+ return getUpgradeRequest(groupName, false);
+ }
+
+ /**
+ * The resource id for the upgrade request message for a permission group for a specific device.
+ *
+ * @param groupName Permission group name
+ * @return The id or 0 if the permission group doesn't exist or have a message
+ */
+ public static int getUpgradeRequest(String groupName, Boolean isDeviceAwareMessage) {
+ if (isDeviceAwareMessage) {
+ return PERM_GROUP_UPGRADE_REQUEST_DEVICE_AWARE_RES.getOrDefault(groupName, 0);
+ } else {
+ return PERM_GROUP_UPGRADE_REQUEST_RES.getOrDefault(groupName, 0);
+ }
}
/**
@@ -1062,6 +1286,45 @@ public final class Utils {
}
/**
+ * The resource id for the fine location request message for a specific device
+ *
+ * @return The id
+ */
+ public static int getFineLocationRequest(Boolean isDeviceAwareMessage) {
+ if (isDeviceAwareMessage) {
+ return R.string.permgrouprequest_device_aware_fineupgrade;
+ } else {
+ return R.string.permgrouprequest_fineupgrade;
+ }
+ }
+
+ /**
+ * The resource id for the coarse location request message for a specific device
+ *
+ * @return The id
+ */
+ public static int getCoarseLocationRequest(Boolean isDeviceAwareMessage) {
+ if (isDeviceAwareMessage) {
+ return R.string.permgrouprequest_device_aware_coarselocation;
+ } else {
+ return R.string.permgrouprequest_coarselocation;
+ }
+ }
+
+ /**
+ * The resource id for the get more photos request message for a specific device
+ *
+ * @return The id
+ */
+ public static int getMorePhotosRequest(Boolean isDeviceAwareMessage) {
+ if (isDeviceAwareMessage) {
+ return R.string.permgrouprequest_device_aware_more_photos;
+ } else {
+ return R.string.permgrouprequest_more_photos;
+ }
+ }
+
+ /**
* Returns a random session ID value that's guaranteed to not be {@code INVALID_SESSION_ID}.
*
* @return A valid session ID.
@@ -1249,10 +1512,8 @@ public final class Utils {
* Returns if a card should be shown if the sensor is blocked
**/
public static boolean shouldDisplayCardIfBlocked(@NonNull String permissionGroupName) {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_WARNING_BANNER_DISPLAY_ENABLED, true) && (
- CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName)
- || LOCATION.equals(permissionGroupName));
+ return CAMERA.equals(permissionGroupName) || MICROPHONE.equals(permissionGroupName)
+ || LOCATION.equals(permissionGroupName);
}
/**
@@ -1278,6 +1539,13 @@ public final class Utils {
}
/**
+ * Returns the blocked title code on automotive for a permission
+ **/
+ public static int getBlockedTitleAutomotive(@NonNull String permissionGroupName) {
+ return PERM_BLOCKED_TITLE_AUTOMOTIVE.getOrDefault(permissionGroupName, -1);
+ }
+
+ /**
* Returns if the permission group has a background mode, even if the background mode is
* introduced in a platform version after the one currently running
**/
@@ -1339,4 +1607,67 @@ public final class Utils {
@NonNull ApplicationInfo applicationInfo) {
return context.getPackageManager().getApplicationLabel(applicationInfo).toString();
}
+
+ /**
+ * Returns whether the given user should be shown in the Settings UI in SdkLevel V+. This method
+ * will always return true for SdkLevels below V.
+ *
+ * @param userHandle The user for which to check whether it should be shown or not.
+ * @return true if it should be shown, false otherwise.
+ */
+ public static boolean shouldShowInSettings(UserHandle userHandle, UserManager userManager) {
+ return !SdkLevel.isAtLeastV() || shouldShowInSettingsInternal(userHandle, userManager);
+ }
+
+ /**
+ * Returns whether the given user should be shown in the Settings UI.
+ *
+ * @param userHandle The user for which to check whether it should be shown or not.
+ * @return true if it should be shown, false otherwise.
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private static boolean shouldShowInSettingsInternal(
+ UserHandle userHandle, UserManager userManager) {
+ var userProperties = userManager.getUserProperties(userHandle);
+ return !userManager.isQuietModeEnabled(userHandle)
+ || userProperties.getShowInQuietMode() != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN;
+ }
+
+ /**
+ * Check whether an application is restricted for this setting identifier and return the
+ * {@code Intent} for the restriction if it is.
+ *
+ * @param user the user to check for
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the {@code Intent} for the restriction if the application is restricted for this
+ * setting identifier, or {@code null} otherwise.
+ */
+ @Nullable
+ public static Intent getApplicationEnhancedConfirmationRestrictedIntentAsUser(
+ @NonNull UserHandle user,
+ @NonNull Context context,
+ @Nullable String packageName,
+ @Nullable String settingIdentifier) {
+ if (SdkLevel.isAtLeastV() && Flags.enhancedConfirmationModeApisEnabled()) {
+ Context userContext = Utils.getUserContext(context, user);
+ EnhancedConfirmationManager userEnhancedConfirmationManager =
+ userContext.getSystemService(EnhancedConfirmationManager.class);
+ if (packageName == null || settingIdentifier == null) return null;
+ try {
+ boolean isRestricted = userEnhancedConfirmationManager.isRestricted(packageName,
+ settingIdentifier);
+ if (isRestricted) {
+ return userEnhancedConfirmationManager.createRestrictedSettingDialogIntent(
+ packageName, settingIdentifier);
+ }
+
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "Cannot check enhanced confirmation restriction for package: "
+ + packageName, e);
+ }
+ }
+ return null;
+ }
}
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..b654b7e1c 100644
--- a/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v34/SafetyLabelUtils.kt
@@ -16,6 +16,11 @@
package com.android.permissioncontroller.permission.utils.v34
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.APP_METADATA_SOURCE_APK
+import android.util.Log
+import com.android.modules.utils.build.SdkLevel
import com.android.permission.safetylabel.DataCategory
import com.android.permission.safetylabel.DataType
import com.android.permission.safetylabel.DataTypeConstants
@@ -23,6 +28,8 @@ import com.android.permission.safetylabel.SafetyLabel
import com.android.permissioncontroller.permission.utils.PermissionMapping
object SafetyLabelUtils {
+ private val LOG_TAG = SafetyLabelUtils::class.java.simpleName
+
/*
* Get the sharing purposes for a SafetyLabel related to a specific permission group.
*/
@@ -32,8 +39,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) {
@@ -55,4 +62,25 @@ object SafetyLabelUtils {
return purposeSet
}
+
+ /**
+ * Returns the {@code TRUE} if [AppMetadataSource] for the given package is
+ * supported for permission rationale, as well as for U- where getAppMetadataSource isn't
+ * available.
+ */
+ fun isAppMetadataSourceSupported(userContext: Context, packageName: String): Boolean {
+ if (!SdkLevel.isAtLeastV() || !android.content.pm.Flags.aslInApkAppMetadataSource()) {
+ // PackageManager.getAppMetadataSource() is not available and ASL in APK is ignored in
+ // U and below. We can assume it came from oem/pre-install or installer source (app
+ // store). Treat this as AppMetadataSource allowed.
+ return true
+ }
+
+ return try {
+ userContext.packageManager.getAppMetadataSource(packageName) != APP_METADATA_SOURCE_APK
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(LOG_TAG, "AppMetadataSource for $packageName not found")
+ false
+ }
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/permission/utils/v35/MultiDeviceUtils.kt b/PermissionController/src/com/android/permissioncontroller/permission/utils/v35/MultiDeviceUtils.kt
new file mode 100644
index 000000000..ea63a96b8
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/permission/utils/v35/MultiDeviceUtils.kt
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.permission.utils.v35
+
+import android.Manifest
+import android.app.Application
+import android.companion.virtual.VirtualDeviceManager
+import android.content.Context
+import android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
+import android.os.Build
+import android.permission.PermissionManager
+import android.provider.Settings
+import androidx.annotation.ChecksSdkIntAtLeast
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.DeviceUtils
+import com.android.permissioncontroller.permission.utils.ContextCompat
+
+object MultiDeviceUtils {
+ const val DEFAULT_REMOTE_DEVICE_NAME = "remote device"
+
+ /**
+ * Defines what runtime permissions are device aware. This can be replaced with an API from VDM
+ * which can take device's capabilities into account
+ */
+ // TODO: b/298661870 - Use new API to get the list of device aware permissions
+ private val DEVICE_AWARE_PERMISSIONS: Set<String> =
+ setOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
+
+ private const val DEVICE_AWARE_PERMISSION_FLAG_MASK =
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED or
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED or
+ FLAG_PERMISSION_ONE_TIME or
+ FLAG_PERMISSION_USER_SET or
+ FLAG_PERMISSION_USER_FIXED
+
+ @JvmStatic
+ fun isDeviceAwarePermissionSupported(context: Context): Boolean =
+ SdkLevel.isAtLeastV() &&
+ !(DeviceUtils.isTelevision(context) ||
+ DeviceUtils.isAuto(context) ||
+ DeviceUtils.isWear(context))
+
+ @JvmStatic
+ fun isPermissionDeviceAware(permission: String): Boolean =
+ permission in DEVICE_AWARE_PERMISSIONS
+
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun isPermissionDeviceAware(context: Context, deviceId: Int, permission: String): Boolean {
+ if (!SdkLevel.isAtLeastV()) {
+ return false
+ }
+
+ if (permission !in DEVICE_AWARE_PERMISSIONS) {
+ return false
+ }
+
+ val vdm = context.getSystemService(VirtualDeviceManager::class.java) ?: return false
+ val virtualDevice = vdm.getVirtualDevice(deviceId) ?: return false
+
+ return when (permission) {
+ Manifest.permission.CAMERA -> virtualDevice.hasCustomCameraSupport()
+ Manifest.permission.RECORD_AUDIO -> virtualDevice.hasCustomAudioInputSupport()
+ else -> false
+ }
+ }
+
+ @JvmStatic
+ fun getDeviceName(context: Context, deviceId: Int): String? {
+ // Pre Android V no permission requests can affect the VirtualDevice, thus return local
+ // device name.
+ if (!SdkLevel.isAtLeastV() || deviceId == ContextCompat.DEVICE_ID_DEFAULT) {
+ return Settings.Global.getString(context.contentResolver, Settings.Global.DEVICE_NAME)
+ }
+ val vdm: VirtualDeviceManager? = context.getSystemService(VirtualDeviceManager::class.java)
+ if (vdm != null) {
+ val virtualDevice = vdm.getVirtualDevice(deviceId)
+ if (virtualDevice != null) {
+ return if (virtualDevice.displayName != null) virtualDevice.displayName.toString()
+ else DEFAULT_REMOTE_DEVICE_NAME
+ }
+ }
+ throw IllegalArgumentException("No device name for device: $deviceId")
+ }
+
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun isDefaultDeviceId(persistentDeviceId: String?) =
+ !SdkLevel.isAtLeastV() ||
+ persistentDeviceId.isNullOrBlank() ||
+ persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun getDeviceName(context: Context, persistentDeviceId: String): String {
+ if (
+ !SdkLevel.isAtLeastV() ||
+ persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ ) {
+ return Settings.Global.getString(context.contentResolver, Settings.Global.DEVICE_NAME)
+ }
+ val vdm: VirtualDeviceManager =
+ context.getSystemService(VirtualDeviceManager::class.java)
+ ?: throw RuntimeException("VirtualDeviceManager not found")
+ val deviceName =
+ vdm.getDisplayNameForPersistentDeviceId(persistentDeviceId)
+ ?: DEFAULT_REMOTE_DEVICE_NAME
+ return deviceName.toString()
+ }
+
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun getDefaultDevicePersistentDeviceId(): String =
+ if (!SdkLevel.isAtLeastV()) {
+ "default: ${ContextCompat.DEVICE_ID_DEFAULT}"
+ } else {
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ }
+
+ /**
+ * Grants external device permissions to the specified package. Permissions will be extracted
+ * from the group name.
+ *
+ * @param app The current application
+ * @param persistentDeviceId The external device identifier
+ * @param packageName Name of the package to which permission needs to granted
+ * @param permissions Permissions that needs to be granted
+ * @param userSet Whether to mark the permission as user set
+ *
+ * TODO: b/328839130: This method is meant to use it on External Devices and on Device Aware
+ * permissions only. It does not follow the default device implementation because of the
+ * LightAppPermGroup requirement. The data class LightAppPermGroup is not available for
+ * external devices at present, hence the implementation differs.
+ */
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun grantRuntimePermissionsWithPersistentDeviceId(
+ app: Application,
+ persistentDeviceId: String,
+ packageName: String,
+ permissions: Set<String>,
+ userSet: Boolean
+ ) {
+ if (!SdkLevel.isAtLeastV() || isDefaultDeviceId(persistentDeviceId)) {
+ return
+ }
+ permissions
+ .filter { isPermissionDeviceAware(it) }
+ .forEach { permission ->
+ grantRuntimePermissionWithPersistentDeviceId(
+ app,
+ persistentDeviceId,
+ packageName,
+ permission,
+ userSet
+ )
+ }
+ }
+
+ /**
+ * Grants the external device permission to the specified package
+ *
+ * @param app The current application
+ * @param persistentDeviceId The external device identifier
+ * @param packageName Name of the package to which permission needs to granted
+ * @param permission Permission that needs to be granted
+ * @param userSet Whether to mark the permission as user set
+ *
+ * TODO: b/328839130: This method is meant to use it on External Devices and on Device Aware
+ * permissions only. It does not follow the default device implementation because of the
+ * LightAppPermGroup requirement. The data class LightAppPermGroup is not available for
+ * external devices at present, hence the implementation differs.
+ */
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private fun grantRuntimePermissionWithPersistentDeviceId(
+ app: Application,
+ persistentDeviceId: String,
+ packageName: String,
+ permission: String,
+ userSet: Boolean
+ ) {
+ if (!SdkLevel.isAtLeastV() || isDefaultDeviceId(persistentDeviceId)) {
+ return
+ }
+ val permissionManager = app.getSystemService(PermissionManager::class.java)!!
+ var newFlag =
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED or
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
+ if (userSet) {
+ newFlag = newFlag or FLAG_PERMISSION_USER_SET
+ }
+ permissionManager.updatePermissionFlags(
+ packageName,
+ permission,
+ persistentDeviceId,
+ DEVICE_AWARE_PERMISSION_FLAG_MASK,
+ newFlag
+ )
+ permissionManager.grantRuntimePermission(packageName, permission, persistentDeviceId)
+ }
+
+ /**
+ * Revokes the external device permissions from the specified package. Permissions will be
+ * extracted from the group name.
+ *
+ * @param app The current application
+ * @param persistentDeviceId The external device identifier
+ * @param packageName Name of the package to which permission needs to revoked
+ * @param permissions Permissions that needs to be revoked
+ * @param userSet Whether to mark the permission as user set
+ * @param oneTime Whether this is a one-time permission grant permissions
+ * @param reason The reason for the revoke, or {@code null} for unspecified
+ *
+ * TODO: b/328839130: This method is meant to use it on External Devices and on Device Aware
+ * permissions only. It does not follow the default device implementation because of the
+ * LightAppPermGroup requirement. The data class LightAppPermGroup is not available for
+ * external devices at present, hence the implementation differs.
+ */
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ fun revokeRuntimePermissionsWithPersistentDeviceId(
+ app: Application,
+ persistentDeviceId: String,
+ packageName: String,
+ permissions: Set<String>,
+ userSet: Boolean,
+ oneTime: Boolean,
+ reason: String? = null
+ ) {
+ if (!SdkLevel.isAtLeastV() || isDefaultDeviceId(persistentDeviceId)) {
+ return
+ }
+ permissions
+ .filter { isPermissionDeviceAware(it) }
+ .forEach { permission ->
+ revokeRuntimePermissionWithPersistentDeviceId(
+ app,
+ persistentDeviceId,
+ packageName,
+ permission,
+ userSet,
+ oneTime,
+ reason
+ )
+ }
+ }
+
+ /**
+ * Revokes the external device permission to the specified package.
+ *
+ * @param app The current application
+ * @param persistentDeviceId The external device identifier
+ * @param packageName Name of the package to which permission needs to revoked
+ * @param permission Permission that needs to be revoked
+ * @param userSet Whether to mark the permission as user set
+ * @param oneTime Whether this is a one-time permission grant permissions
+ * @param reason The reason for the revoke, or {@code null} for unspecified
+ *
+ * TODO: b/328839130: This method is meant to use it on External Devices and on Device Aware
+ * permissions only. It does not follow the default device implementation because of the
+ * LightAppPermGroup requirement. The data class LightAppPermGroup is not available for
+ * external devices at present, hence the implementation differs.
+ */
+ @JvmStatic
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private fun revokeRuntimePermissionWithPersistentDeviceId(
+ app: Application,
+ persistentDeviceId: String,
+ packageName: String,
+ permission: String,
+ userSet: Boolean,
+ oneTime: Boolean,
+ reason: String? = null
+ ) {
+ if (!SdkLevel.isAtLeastV() || isDefaultDeviceId(persistentDeviceId)) {
+ return
+ }
+ val permissionManager = app.getSystemService(PermissionManager::class.java)!!
+ var newFlag =
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED or
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
+ if (oneTime) {
+ newFlag = newFlag or FLAG_PERMISSION_ONE_TIME
+ }
+ if (userSet) {
+ newFlag = newFlag or FLAG_PERMISSION_USER_SET
+ }
+ if (isPermissionUserFixed(app, persistentDeviceId, packageName, permission) && !oneTime) {
+ newFlag = newFlag or FLAG_PERMISSION_USER_FIXED
+ }
+ permissionManager.updatePermissionFlags(
+ packageName,
+ permission,
+ persistentDeviceId,
+ DEVICE_AWARE_PERMISSION_FLAG_MASK,
+ newFlag
+ )
+ permissionManager.revokeRuntimePermission(
+ packageName,
+ permission,
+ persistentDeviceId,
+ reason
+ )
+ }
+
+ /**
+ * Determines if the permission is UserFixed. This method is for to use with V and above only.
+ * Supports both external and default devices, need to specify persistentDeviceId accordingly.
+ */
+ @ChecksSdkIntAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private fun isPermissionUserFixed(
+ app: Application,
+ persistentDeviceId: String,
+ packageName: String,
+ permission: String
+ ): Boolean {
+ if (!SdkLevel.isAtLeastV()) {
+ return true
+ }
+ val permissionManager = app.getSystemService(PermissionManager::class.java)!!
+ val flags =
+ permissionManager.getPermissionFlags(packageName, permission, persistentDeviceId)
+ return flags and FLAG_PERMISSION_USER_FIXED != 0
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt b/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt
new file mode 100644
index 000000000..faef36f68
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/pm/data/model/v31/PackageInfoModel.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.pm.data.model.v31
+
+import android.content.pm.PackageInfo
+
+/** A model/data class representing [PackageInfo] class. */
+data class PackageInfoModel(
+ val packageName: String,
+ val requestedPermissions: List<String> = emptyList(),
+ val requestedPermissionsFlags: List<Int> = emptyList(),
+ val applicationFlags: Int = 0,
+) {
+ constructor(
+ packageInfo: PackageInfo
+ ) : this(
+ packageInfo.packageName,
+ packageInfo.requestedPermissions?.toList() ?: emptyList(),
+ packageInfo.requestedPermissionsFlags?.toList() ?: emptyList(),
+ requireNotNull(packageInfo.applicationInfo).flags
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt b/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt
new file mode 100644
index 000000000..40c714f55
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/pm/data/repository/v31/PackageRepository.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.pm.data.repository.v31
+
+import android.app.Application
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.util.Log
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel
+import kotlin.concurrent.Volatile
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+ * Repository to access package info data exposed by [PackageManager]. Domain and view layer
+ * shouldn't access [PackageManager] directly, instead they should use the repository.
+ */
+interface PackageRepository {
+ suspend fun getPackageInfo(
+ packageName: String,
+ user: UserHandle,
+ flags: Int = PackageManager.GET_PERMISSIONS
+ ): PackageInfoModel?
+
+ companion object {
+ @Volatile private var instance: PackageRepository? = null
+
+ fun getInstance(app: Application): PackageRepository =
+ instance ?: synchronized(this) { PackageRepositoryImpl(app).also { instance = it } }
+ }
+}
+
+class PackageRepositoryImpl(
+ private val app: Application,
+ private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
+) : PackageRepository {
+ override suspend fun getPackageInfo(
+ packageName: String,
+ user: UserHandle,
+ flags: Int
+ ): PackageInfoModel? =
+ withContext(dispatcher) {
+ try {
+ val packageInfo =
+ Utils.getUserContext(app, user)
+ .packageManager
+ .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
+ PackageInfoModel(packageInfo)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(LOG_TAG, "package $packageName not found for user ${user.identifier}")
+ null
+ }
+ }
+
+ companion object {
+ private const val LOG_TAG = "PackageRepository"
+ }
+}
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..c633c013a 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
@@ -74,12 +76,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
-@VisibleForTesting
-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)
@@ -87,17 +86,7 @@ private fun isAccessibilitySourceSupported(): Boolean {
return SdkLevel.isAtLeastT()
}
-fun isAccessibilitySourceEnabled(): Boolean {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_SC_ACCESSIBILITY_SOURCE_ENABLED,
- true
- )
-}
-
-/**
- * cts test needs to disable the listener.
- */
+/** cts test needs to disable the listener. */
fun isAccessibilityListenerEnabled(): Boolean {
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
@@ -108,27 +97,24 @@ 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(
+ suspend fun processAccessibilityJob(
params: JobParameters?,
jobService: AccessibilityJobService,
cancel: BooleanSupplier?
@@ -140,14 +126,12 @@ class AccessibilitySourceService(
sessionId = random.nextLong()
}
if (DEBUG) {
- Log.v(LOG_TAG, "safety center accessibility privacy job started.")
+ Log.d(LOG_TAG, "safety center accessibility privacy job started.")
}
interruptJobIfCanceled(cancel)
val a11yServiceList = getEnabledAccessibilityServices()
if (a11yServiceList.isEmpty()) {
- if (DEBUG) {
- Log.v(LOG_TAG, "accessibility services not enabled, job completed.")
- }
+ Log.d(LOG_TAG, "accessibility services not enabled, job completed.")
jobService.jobFinished(params, false)
jobService.clearJob()
return
@@ -155,19 +139,19 @@ 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) {
- Log.v(LOG_TAG, "sending an accessibility service notification")
+ Log.d(LOG_TAG, "sending an accessibility service notification")
}
val serviceToBeNotified: AccessibilityServiceInfo =
toBeNotifiedServices[random.nextInt(toBeNotifiedServices.size)]
@@ -192,9 +176,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 +194,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 +216,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,15 +239,15 @@ 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)!!)
if (DEBUG) {
- Log.v(LOG_TAG, "NOTIF_INTERACTION SEND metric, uid $uid session $sessionId")
+ Log.d(LOG_TAG, "NOTIF_INTERACTION SEND metric, uid $uid session $sessionId")
}
PermissionControllerStatsLog.write(
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION,
@@ -273,11 +260,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 +278,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 +323,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 +352,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 +363,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 +378,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 +401,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 +426,10 @@ class AccessibilitySourceService(
)
}
+ private fun getSafetySourceIssueId(componentName: ComponentName): String {
+ return "accessibility_${componentName.flattenToString()}"
+ }
+
private fun sendIssuesToSafetyCenter(
a11yServiceList: List<AccessibilityServiceInfo>,
sessionId: Long,
@@ -441,9 +439,7 @@ class AccessibilitySourceService(
val dataBuilder = SafetySourceData.Builder()
pendingIssues.forEach { dataBuilder.addIssue(it) }
val safetySourceData = dataBuilder.build()
- if (DEBUG) {
- Log.v(LOG_TAG, "sending ${pendingIssues.size} issue to sc, data: $safetySourceData")
- }
+ Log.d(LOG_TAG, "a11y source sending ${pendingIssues.size} issue to sc")
safetyCenterManager.setSafetySourceData(
SC_ACCESSIBILITY_SOURCE_ID,
safetySourceData,
@@ -467,9 +463,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 +471,31 @@ 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 }
+ val enabled3rdPartyServices =
+ enabledServices.filterNotNull().filter { !it.isAccessibilityTool }
+ Log.d(LOG_TAG, "enabled a11y services count ${enabledServices.size}")
+ return enabled3rdPartyServices
}
/**
@@ -509,25 +508,28 @@ class AccessibilitySourceService(
return notifications.firstOrNull { it.id == Constants.ACCESSIBILITY_CHECK_NOTIFICATION_ID }
}
- internal suspend fun removeFromNotifiedServices(a11Service: ComponentName) {
+ 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()
}
}
}
- internal suspend fun markServiceAsNotified(a11Service: ComponentName) {
+ suspend fun markServiceAsNotified(a11Service: ComponentName) {
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 +539,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()
}
}
@@ -548,21 +552,17 @@ class AccessibilitySourceService(
}
@VisibleForTesting
- internal fun getSharedPreference(): SharedPreferences {
+ fun getSharedPreference(): SharedPreferences {
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 +571,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 +581,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) {
@@ -598,20 +594,22 @@ class AccessibilitySourceService(
.cancel(notificationTag, Constants.ACCESSIBILITY_CHECK_NOTIFICATION_ID)
}
- internal suspend fun removePackageState(pkg: String) {
+ 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 +627,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 +654,6 @@ class AccessibilitySourceService(
/**
* Flexibility of the periodic check.
*
- *
* 10% of [.getPeriodicCheckIntervalMillis]
*
* @return The flexibility of the periodic check in milliseconds
@@ -668,7 +665,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
@@ -692,7 +688,7 @@ class AccessibilitySourceService(
refreshEvent: RefreshEvent
) {
if (DEBUG) {
- Log.v(LOG_TAG, "rescan and push event from safety center $refreshEvent")
+ Log.d(LOG_TAG, "rescan and push event from safety center $refreshEvent")
}
val safetyCenterEvent = getSafetyCenterEvent(refreshEvent, intent)
sendIssuesToSafetyCenter(safetyCenterEvent)
@@ -705,8 +701,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
}
@@ -719,11 +716,9 @@ class AccessibilityPackageResetHandler : BroadcastReceiver() {
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
coroutineScope.launch(Dispatchers.Default) {
if (DEBUG) {
- Log.v(LOG_TAG, "package reset event occurred for ${data.schemeSpecificPart}")
- }
- AccessibilitySourceService(context).run {
- removePackageState(data.schemeSpecificPart)
+ Log.d(LOG_TAG, "package reset event occurred for ${data.schemeSpecificPart}")
}
+ AccessibilitySourceService(context).run { removePackageState(data.schemeSpecificPart) }
}
}
}
@@ -738,7 +733,7 @@ class AccessibilityNotificationDeleteHandler : BroadcastReceiver() {
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
coroutineScope.launch(Dispatchers.Default) {
if (DEBUG) {
- Log.v(LOG_TAG, "NOTIF_INTERACTION DISMISSED metric, uid $uid session $sessionId")
+ Log.d(LOG_TAG, "NOTIF_INTERACTION DISMISSED metric, uid $uid session $sessionId")
}
PermissionControllerStatsLog.write(
PRIVACY_SIGNAL_NOTIFICATION_INTERACTION,
@@ -751,9 +746,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
@@ -767,36 +760,36 @@ class AccessibilityRemoveAccessHandler : BroadcastReceiver() {
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
coroutineScope.launch(Dispatchers.Default) {
if (DEBUG) {
- Log.v(LOG_TAG, "disabling a11y service ${a11yService.flattenToShortString()}")
+ Log.d(LOG_TAG, "disabling a11y service ${a11yService.flattenToShortString()}")
}
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) {
- Log.v(LOG_TAG, "ISSUE_CARD_INTERACTION CTA1 metric, uid $uid session $sessionId")
+ Log.d(LOG_TAG, "ISSUE_CARD_INTERACTION CTA1 metric, uid $uid session $sessionId")
}
PermissionControllerStatsLog.write(
PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION,
@@ -809,9 +802,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
@@ -825,7 +816,7 @@ class AccessibilityWarningCardDismissalReceiver : BroadcastReceiver() {
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
coroutineScope.launch(Dispatchers.Default) {
if (DEBUG) {
- Log.v(LOG_TAG, "removing notification for ${componentName.flattenToShortString()}")
+ Log.d(LOG_TAG, "removing notification for ${componentName.flattenToShortString()}")
}
val accessibilityService = AccessibilitySourceService(context)
accessibilityService.removeAccessibilityNotification(componentName)
@@ -833,7 +824,7 @@ class AccessibilityWarningCardDismissalReceiver : BroadcastReceiver() {
}
if (DEBUG) {
- Log.v(LOG_TAG, "ISSUE_CARD_INTERACTION DISMISSED metric, uid $uid session $sessionId")
+ Log.d(LOG_TAG, "ISSUE_CARD_INTERACTION DISMISSED metric, uid $uid session $sessionId")
}
PermissionControllerStatsLog.write(
PRIVACY_SIGNAL_ISSUE_CARD_INTERACTION,
@@ -846,8 +837,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() {
@@ -855,25 +846,26 @@ class AccessibilityOnBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (!isAccessibilitySourceSupported() || isProfile(context)) {
- Log.v(LOG_TAG, "accessibility privacy job not supported, can't schedule the job")
+ Log.i(LOG_TAG, "accessibility privacy job not supported, can't schedule the job")
return
}
if (DEBUG) {
- Log.v(LOG_TAG, "scheduling safety center accessibility privacy source job")
+ Log.d(LOG_TAG, "scheduling safety center accessibility privacy source job")
}
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 +882,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()
@@ -903,27 +894,28 @@ class AccessibilityJobService : JobService() {
Log.v(LOG_TAG, "accessibility privacy source job started.")
synchronized(mLock) {
if (mCurrentJob != null) {
- Log.v(LOG_TAG, "Accessibility privacy source job already running")
+ Log.i(LOG_TAG, "Accessibility privacy source job already running")
return false
}
- if (!isAccessibilitySourceEnabled() ||
- !isSafetyCenterEnabled(this@AccessibilityJobService)) {
- Log.v(LOG_TAG, "either privacy source or safety center is not enabled")
+ if (!isSafetyCenterEnabled(this@AccessibilityJobService)) {
+ Log.i(LOG_TAG, "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 +923,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 }
}
}
@@ -956,28 +947,31 @@ class SafetyCenterAccessibilityListener(val context: Context) :
override fun onAccessibilityServicesStateChanged(manager: AccessibilityManager) {
if (!isAccessibilityListenerEnabled()) {
- Log.v(LOG_TAG, "accessibility event occurred, listener not enabled.")
+ Log.i(LOG_TAG, "accessibility event occurred, listener not enabled.")
return
}
- if (!isAccessibilitySourceEnabled() || !isSafetyCenterEnabled(context) ||
- isProfile(context)) {
- Log.v(LOG_TAG, "accessibility event occurred, safety center feature not enabled.")
+ if (!isSafetyCenterEnabled(context) || isProfile(context)) {
+ Log.i(LOG_TAG, "accessibility event occurred, safety center feature not enabled.")
return
}
val coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob())
coroutineScope.launch(Dispatchers.Default) {
if (DEBUG) {
- Log.v(LOG_TAG, "processing accessibility event")
+ Log.d(LOG_TAG, "processing accessibility event")
}
AccessibilitySourceService.lock.withLock {
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..43b3edc04 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,15 +182,15 @@ 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
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@VisibleForTesting
-internal class NotificationListenerCheckInternal(
+class NotificationListenerCheckInternal(
context: Context,
private val shouldCancel: BooleanSupplier?
) {
@@ -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()
@@ -249,7 +255,7 @@ internal class NotificationListenerCheckInternal(
* <p>Always run async inside a {@NotificationListenerCheckJobService} via coroutine.
*/
@WorkerThread
- internal suspend fun getEnabledNotificationListenersAndNotifyIfNeeded(
+ suspend fun getEnabledNotificationListenersAndNotifyIfNeeded(
params: JobParameters,
service: NotificationListenerCheckJobService
) {
@@ -282,11 +288,12 @@ internal class NotificationListenerCheckInternal(
sessionId = random.nextLong()
}
if (DEBUG) {
- Log.v(
+ Log.d(
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()
@@ -335,11 +343,11 @@ internal class NotificationListenerCheckInternal(
}
@VisibleForTesting
- internal fun getNotifiedComponents(): MutableSet<String> {
+ fun getNotifiedComponents(): MutableSet<String> {
return sharedPrefs.getStringSet(KEY_ALREADY_NOTIFIED_COMPONENTS, mutableSetOf<String>())!!
}
- internal suspend fun removeDisabledComponentsFromNotifiedComponents(
+ suspend fun removeDisabledComponentsFromNotifiedComponents(
enabledComponents: Collection<ComponentName>
) {
sharedPrefsLock.withLock {
@@ -356,7 +364,7 @@ internal class NotificationListenerCheckInternal(
}
}
- internal suspend fun markComponentAsNotified(component: ComponentName) {
+ suspend fun markComponentAsNotified(component: ComponentName) {
sharedPrefsLock.withLock {
val notifiedComponents = getNotifiedComponents()
notifiedComponents.add(component.flattenToShortString())
@@ -367,7 +375,7 @@ internal class NotificationListenerCheckInternal(
}
}
- internal suspend fun removeFromNotifiedComponents(packageName: String) {
+ suspend fun removeFromNotifiedComponents(packageName: String) {
sharedPrefsLock.withLock {
val notifiedComponents = getNotifiedComponents()
val filteredServices =
@@ -386,7 +394,7 @@ internal class NotificationListenerCheckInternal(
}
}
- internal suspend fun removeFromNotifiedComponents(component: ComponentName) {
+ suspend fun removeFromNotifiedComponents(component: ComponentName) {
val componentNameShortString = component.flattenToShortString()
sharedPrefsLock.withLock {
val notifiedComponents = getNotifiedComponents()
@@ -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(
+ Log.d(
TAG,
"Notification not posted, within " +
- "$DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS ms")
+ "$DEFAULT_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS ms"
+ )
}
return
}
@@ -435,7 +446,7 @@ internal class NotificationListenerCheckInternal(
// Check for existing notification first, exit if one already present
if (getCurrentlyShownNotificationLocked() != null) {
if (DEBUG) {
- Log.v(TAG, "Notification not posted, previous notification has not been dismissed")
+ Log.d(TAG, "Notification not posted, previous notification has not been dismissed")
}
return
}
@@ -448,7 +459,7 @@ internal class NotificationListenerCheckInternal(
if (componentsInternal.isEmpty()) {
if (DEBUG) {
- Log.v(TAG, "Notification not posted, no unnotified enabled listeners")
+ Log.d(TAG, "Notification not posted, no unnotified enabled listeners")
}
return
}
@@ -456,9 +467,10 @@ internal class NotificationListenerCheckInternal(
componentToNotifyFor = componentsInternal[random.nextInt(componentsInternal.size)]
try {
if (DEBUG) {
- Log.v(
+ Log.d(
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,9 +516,8 @@ internal class NotificationListenerCheckInternal(
pkg: PackageInfo,
sessionId: Long
) {
- val pkgLabel =
- Utils.getApplicationLabel(parentUserContext, pkg.applicationInfo)
- val uid = pkg.applicationInfo.uid
+ val pkgLabel = Utils.getApplicationLabel(parentUserContext, pkg.applicationInfo!!)
+ val uid = pkg.applicationInfo!!.uid
val deletePendingIntent =
getNotificationDeletePendingIntent(parentUserContext, componentName, uid, sessionId)
@@ -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(
+ Log.d(
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? {
@@ -697,36 +726,51 @@ internal class NotificationListenerCheckInternal(
}
return null
}
- val pkgLabel = Utils.getApplicationLabel(parentUserContext, pkgInfo.applicationInfo)
+ val pkgLabel = Utils.getApplicationLabel(parentUserContext, pkgInfo.applicationInfo!!)
val safetySourceIssueId = getSafetySourceIssueIdFromComponentName(componentName)
- val uid = pkgInfo.applicationInfo.uid
+ val uid = pkgInfo.applicationInfo!!.uid
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")
}
@@ -954,17 +1005,19 @@ class NotificationListenerCheckNotificationDeleteHandler : BroadcastReceiver() {
NotificationListenerCheckInternal(context, null).markComponentAsNotified(componentName)
}
if (DEBUG) {
- Log.v(
+ Log.d(
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
+ )
}
}
@@ -981,10 +1034,11 @@ class DisableNotificationListenerComponentHandler : BroadcastReceiver() {
GlobalScope.launch(Default) {
if (DEBUG) {
- Log.v(
+ Log.d(
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
+ )
}
}
@@ -1041,10 +1099,11 @@ class NotificationListenerActionCardDismissalReceiver : BroadcastReceiver() {
GlobalScope.launch(Default) {
if (DEBUG) {
- Log.v(
+ Log.d(
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/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING
index dc01ab3e2..3e8c5a19c 100644
--- a/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/privacysources/TEST_MAPPING
@@ -36,5 +36,21 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsPermissionUiTestCases"
+ },
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.NotificationListenerCheckTest"
+ },
+ {
+ "include-filter": "android.permission.cts.AccessibilityPrivacySourceTest"
+ }
+ ]
+ }
]
}
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/Role.md b/PermissionController/src/com/android/permissioncontroller/role/Role.md
index bde9f86f0..acdfffb50 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/Role.md
+++ b/PermissionController/src/com/android/permissioncontroller/role/Role.md
@@ -65,6 +65,10 @@ title. This attribute is required if the role is `visible`.
Android S. This attribute is optional and defaults to `Build.VERSION_CODES.CUR_DEVELOPMENT`.
- `minSdkVersion`: The minimum SDK version for the role to be available (inclusive), e.g. `31` for
Android S. This attribute is optional and defaults to `Build.VERSION_CODES.BASE`.
+- `onlyGrantWhenAdded`: Whether the role should only grant privileges when a role holder is actively
+added. This attribute is optional and defaults to `false`.
+- `overrideUserWhenGranting`: Whether the role should override user's choice about privileges when
+granting. This attribute is optional and defaults to `false`.
- `requestDescription`: The string resource for the description in the request role dialog, e.g.
`@string/role_sms_request_description`, which says "Gets access to contacts, SMS, phone". This
description should describe to the user the privileges that are going to be granted, and should not
diff --git a/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING
index d7718a2f2..46b148e68 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/role/TEST_MAPPING
@@ -21,7 +21,43 @@
"exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
},
{
- "exclude-annotation": "androidx.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsRoleTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
}
]
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/data/repository/v31/RoleRepository.kt b/PermissionController/src/com/android/permissioncontroller/role/data/repository/v31/RoleRepository.kt
new file mode 100644
index 000000000..d2d89e817
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/data/repository/v31/RoleRepository.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.role.data.repository.v31
+
+import android.app.Application
+import android.app.role.RoleManager
+import kotlin.concurrent.Volatile
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+ * This repository encapsulates roles data (i.e. querying/adding role holders) exposed by
+ * [RoleManager].
+ */
+interface RoleRepository {
+ /**
+ * @return Set of package names, the usage of private data by these packages is not shown in the
+ * privacy dashboard.
+ */
+ suspend fun getExemptedPackages(): Set<String>
+
+ companion object {
+ @Volatile private var instance: RoleRepository? = null
+
+ fun getInstance(application: Application): RoleRepository =
+ instance
+ ?: synchronized(this) { RoleRepositoryImpl(application).also { instance = it } }
+ }
+}
+
+class RoleRepositoryImpl(application: Application) : RoleRepository {
+ private val roleManager = application.getSystemService(RoleManager::class.java)!!
+
+ override suspend fun getExemptedPackages(): Set<String> =
+ withContext(Dispatchers.Default) {
+ return@withContext buildSet {
+ add(OS_PKG)
+ addAll(EXEMPTED_ROLES.map { role -> roleManager.getRoleHolders(role) }.flatten())
+ }
+ }
+
+ companion object {
+ private const val OS_PKG = "android"
+ private const val SYSTEM_AMBIENT_AUDIO_INTELLIGENCE =
+ "android.app.role.SYSTEM_AMBIENT_AUDIO_INTELLIGENCE"
+ private const val SYSTEM_UI_INTELLIGENCE = "android.app.role.SYSTEM_UI_INTELLIGENCE"
+ private const val SYSTEM_AUDIO_INTELLIGENCE = "android.app.role.SYSTEM_AUDIO_INTELLIGENCE"
+ private const val SYSTEM_NOTIFICATION_INTELLIGENCE =
+ "android.app.role.SYSTEM_NOTIFICATION_INTELLIGENCE"
+ private const val SYSTEM_TEXT_INTELLIGENCE = "android.app.role.SYSTEM_TEXT_INTELLIGENCE"
+ private const val SYSTEM_VISUAL_INTELLIGENCE = "android.app.role.SYSTEM_VISUAL_INTELLIGENCE"
+
+ private val EXEMPTED_ROLES =
+ arrayOf(
+ SYSTEM_AMBIENT_AUDIO_INTELLIGENCE,
+ SYSTEM_UI_INTELLIGENCE,
+ SYSTEM_AUDIO_INTELLIGENCE,
+ SYSTEM_NOTIFICATION_INTELLIGENCE,
+ SYSTEM_TEXT_INTELLIGENCE,
+ SYSTEM_VISUAL_INTELLIGENCE
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java b/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java
index 1c845ee3a..870e2c03a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/service/RoleSearchIndexablesProvider.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.Binder;
+import android.os.Process;
import android.provider.SearchIndexablesContract;
import android.util.ArrayMap;
@@ -28,7 +29,6 @@ import androidx.annotation.Nullable;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.service.BaseSearchIndexablesProvider;
import com.android.permissioncontroller.role.model.RoleParserInitializer;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -61,8 +61,8 @@ public class RoleSearchIndexablesProvider extends BaseSearchIndexablesProvider {
long token = Binder.clearCallingIdentity();
try {
- if (!role.isAvailable(context) || !RoleUiBehaviorUtils.isVisible(role,
- context)) {
+ if (!role.isAvailableAsUser(Process.myUserHandle(), context)
+ || !role.isVisibleAsUser(Process.myUserHandle(), context)) {
continue;
}
} finally {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/CheckableLinearLayout.java b/PermissionController/src/com/android/permissioncontroller/role/ui/CheckableLinearLayout.java
index b396c3b7b..32a0bb20b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/CheckableLinearLayout.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/CheckableLinearLayout.java
@@ -100,12 +100,10 @@ public class CheckableLinearLayout extends LinearLayout implements Checkable {
int count = viewGroup.getChildCount();
for (int i = 0; i < count; i++) {
View child = viewGroup.getChildAt(i);
- if (child.isDuplicateParentStateEnabled()) {
- if (child instanceof Checkable) {
- ((Checkable) child).setChecked(checked);
- } else if (child instanceof ViewGroup) {
- updateChildrenChecked((ViewGroup) child, checked);
- }
+ if (child instanceof Checkable) {
+ ((Checkable) child).setChecked(checked);
+ } else if (child instanceof ViewGroup) {
+ updateChildrenChecked((ViewGroup) child, checked);
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java
index 52471cb32..41f1a06a9 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java
@@ -31,7 +31,7 @@ import com.android.permissioncontroller.DeviceUtils;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.role.ui.auto.AutoDefaultAppFragment;
import com.android.permissioncontroller.role.ui.handheld.HandheldDefaultAppFragment;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.permissioncontroller.role.ui.wear.WearDefaultAppFragment;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -88,7 +88,7 @@ public class DefaultAppActivity extends SettingsActivity {
return;
}
- if (!RoleUiBehaviorUtils.isVisibleAsUser(role, user, this)) {
+ if (!role.isVisibleAsUser(user, this)) {
Log.e(LOG_TAG, "Role is invisible: " + roleName);
finish();
return;
@@ -98,6 +98,8 @@ public class DefaultAppActivity extends SettingsActivity {
Fragment fragment;
if (DeviceUtils.isAuto(this)) {
fragment = AutoDefaultAppFragment.newInstance(roleName, user);
+ } else if (DeviceUtils.isWear(this)) {
+ fragment = WearDefaultAppFragment.Companion.newInstance(roleName, user);
} else {
fragment = HandheldDefaultAppFragment.newInstance(roleName, user);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
index a8b16c521..02d92980b 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
@@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProviders;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
@@ -112,8 +112,9 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
mRole = Roles.get(activity).get(mRoleName);
preferenceFragment.setTitle(getString(mRole.getLabelResource()));
- mViewModel = ViewModelProviders.of(this, new DefaultAppViewModel.Factory(mRole, mUser,
- activity.getApplication())).get(DefaultAppViewModel.class);
+ ViewModelProvider.Factory viewModelFactory = new DefaultAppViewModel.Factory(mRole, mUser,
+ activity.getApplication());
+ mViewModel = new ViewModelProvider(this, viewModelFactory).get(DefaultAppViewModel.class);
mViewModel.getRoleLiveData().observe(this, this::onRoleChanged);
mViewModel.getManageRoleHolderStateLiveData().observe(this,
this::onManageRoleHolderStateChanged);
@@ -214,6 +215,8 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
preference.setChecked(checked);
if (applicationInfo != null) {
+ roleApplicationPreference.setRestrictionIntent(
+ mRole.getApplicationRestrictionIntentAsUser(applicationInfo, mUser, context));
RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference,
applicationInfo, mUser, context);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListActivity.java
index d9cb6dca8..58f35705c 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListActivity.java
@@ -25,6 +25,7 @@ import com.android.permissioncontroller.DeviceUtils;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.role.ui.auto.AutoDefaultAppListFragment;
import com.android.permissioncontroller.role.ui.handheld.HandheldDefaultAppListFragment;
+import com.android.permissioncontroller.role.ui.wear.WearDefaultAppListFragment;
/**
* Activity for the list of default apps.
@@ -45,6 +46,8 @@ public class DefaultAppListActivity extends SettingsActivity {
Fragment fragment;
if (DeviceUtils.isAuto(this)) {
fragment = AutoDefaultAppListFragment.newInstance();
+ } else if (DeviceUtils.isWear(this)) {
+ fragment = WearDefaultAppListFragment.Companion.newInstance();
} else {
fragment = HandheldDefaultAppListFragment.newInstance();
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
index f9a0193bd..d9ce664d8 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
@@ -30,7 +30,7 @@ import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProviders;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
@@ -65,6 +65,8 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
DefaultAppListChildFragment.class.getName() + ".preference.MANAGE_DOMAIN_URLS";
private static final String PREFERENCE_KEY_WORK_CATEGORY =
DefaultAppListChildFragment.class.getName() + ".preference.WORK_CATEGORY";
+ private static final String PREFERENCE_KEY_PRIVATE_CATEGORY =
+ DefaultAppListChildFragment.class.getName() + ".preference.PRIVATE_CATEGORY";
@NonNull
private DefaultAppListViewModel mViewModel;
@@ -82,12 +84,14 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
-
- mViewModel = ViewModelProviders.of(this).get(DefaultAppListViewModel.class);
+ mViewModel = new ViewModelProvider(this).get(DefaultAppListViewModel.class);
mViewModel.getLiveData().observe(this, roleItems -> onRoleListChanged());
if (mViewModel.hasWorkProfile()) {
mViewModel.getWorkLiveData().observe(this, roleItems -> onRoleListChanged());
}
+ if (mViewModel.hasPrivateProfile()) {
+ mViewModel.getPrivateLiveData().observe(this, roleItems -> onRoleListChanged());
+ }
}
private void onRoleListChanged() {
@@ -103,6 +107,14 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
return;
}
}
+ boolean hasPrivateProfile = mViewModel.hasPrivateProfile();
+ List<RoleItem> privateRoleItems = null;
+ if (hasPrivateProfile) {
+ privateRoleItems = mViewModel.getPrivateLiveData().getValue();
+ if (privateRoleItems == null) {
+ return;
+ }
+ }
PF preferenceFragment = requirePreferenceFragment();
PreferenceManager preferenceManager = preferenceFragment.getPreferenceManager();
@@ -111,17 +123,22 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
ArrayMap<String, Preference> oldPreferences = new ArrayMap<>();
PreferenceCategory oldWorkPreferenceCategory = null;
ArrayMap<String, Preference> oldWorkPreferences = new ArrayMap<>();
+ PreferenceCategory oldPrivatePreferenceCategory = null;
+ ArrayMap<String, Preference> oldPrivatePreferences = new ArrayMap<>();
if (preferenceScreen == null) {
preferenceScreen = preferenceManager.createPreferenceScreen(context);
preferenceFragment.setPreferenceScreen(preferenceScreen);
} else {
- oldWorkPreferenceCategory = preferenceScreen.findPreference(
- PREFERENCE_KEY_WORK_CATEGORY);
- if (oldWorkPreferenceCategory != null) {
- clearPreferences(oldWorkPreferenceCategory, oldWorkPreferences);
- preferenceScreen.removePreference(oldWorkPreferenceCategory);
- oldWorkPreferenceCategory.setOrder(Preference.DEFAULT_ORDER);
- }
+ oldWorkPreferenceCategory =
+ preferenceScreen.findPreference(PREFERENCE_KEY_WORK_CATEGORY);
+ clearPreferenceCategory(
+ oldWorkPreferenceCategory, preferenceScreen, oldWorkPreferences);
+
+ oldPrivatePreferenceCategory =
+ preferenceScreen.findPreference(PREFERENCE_KEY_PRIVATE_CATEGORY);
+ clearPreferenceCategory(
+ oldPrivatePreferenceCategory, preferenceScreen, oldPrivatePreferences);
+
clearPreferences(preferenceScreen, oldPreferences);
}
@@ -130,22 +147,34 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
addMoreDefaultAppsPreference(preferenceScreen, oldPreferences, context);
addManageDomainUrlsPreference(preferenceScreen, oldPreferences, context);
if (hasWorkProfile && !workRoleItems.isEmpty()) {
- PreferenceCategory workPreferenceCategory = oldWorkPreferenceCategory;
- if (workPreferenceCategory == null) {
- workPreferenceCategory = new PreferenceCategory(context);
- workPreferenceCategory.setKey(PREFERENCE_KEY_WORK_CATEGORY);
- workPreferenceCategory.setTitle(Utils.getEnterpriseString(context,
- DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE,
- R.string.default_apps_for_work));
- }
- preferenceScreen.addPreference(workPreferenceCategory);
- addPreferences(workPreferenceCategory, workRoleItems, oldWorkPreferences, this,
+ String workTitle = Utils.getEnterpriseString(context,
+ DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE,
+ R.string.default_apps_for_work);
+ addPreferenceCategory(oldWorkPreferenceCategory, PREFERENCE_KEY_WORK_CATEGORY,
+ workTitle, preferenceScreen, workRoleItems, oldWorkPreferences, this,
mViewModel.getWorkProfile(), context);
}
+ if (hasPrivateProfile && !privateRoleItems.isEmpty()) {
+ String privateTitle = context.getString(R.string.default_apps_for_private_profile);
+ addPreferenceCategory(oldPrivatePreferenceCategory, PREFERENCE_KEY_PRIVATE_CATEGORY,
+ privateTitle, preferenceScreen, privateRoleItems, oldPrivatePreferences, this,
+ mViewModel.getPrivateProfile(), context);
+ }
preferenceFragment.onPreferenceScreenChanged();
}
+ private static void clearPreferenceCategory(@Nullable PreferenceCategory preferenceCategory,
+ @NonNull PreferenceScreen preferenceScreen,
+ @NonNull ArrayMap<String, Preference> oldPreferences) {
+ if (preferenceCategory == null) {
+ return;
+ }
+ clearPreferences(preferenceCategory, oldPreferences);
+ preferenceScreen.removePreference(preferenceCategory);
+ preferenceCategory.setOrder(Preference.DEFAULT_ORDER);
+ }
+
private static void clearPreferences(@NonNull PreferenceGroup preferenceGroup,
@NonNull ArrayMap<String, Preference> oldPreferences) {
for (int i = preferenceGroup.getPreferenceCount() - 1; i >= 0; --i) {
@@ -157,6 +186,24 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
}
}
+ @NonNull
+ private void addPreferenceCategory(
+ @Nullable PreferenceCategory oldPreferenceCategory, @NonNull String key,
+ @Nullable String title, @NonNull PreferenceScreen preferenceScreen,
+ @NonNull List<RoleItem> roleItems, @NonNull ArrayMap<String, Preference> oldPreferences,
+ @NonNull Preference.OnPreferenceClickListener listener,
+ @NonNull UserHandle user, @NonNull Context context) {
+ PreferenceCategory preferenceCategory = oldPreferenceCategory;
+ if (preferenceCategory == null) {
+ preferenceCategory = new PreferenceCategory(context);
+ preferenceCategory.setKey(key);
+ preferenceCategory.setTitle(title);
+ }
+ preferenceScreen.addPreference(preferenceCategory);
+ addPreferences(preferenceCategory, roleItems, oldPreferences, listener,
+ user, context);
+ }
+
private void addPreferences(@NonNull PreferenceGroup preferenceGroup,
@NonNull List<RoleItem> roleItems, @NonNull ArrayMap<String, Preference> oldPreferences,
@NonNull Preference.OnPreferenceClickListener listener, @NonNull UserHandle user,
@@ -182,6 +229,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
preference = rolePreference.asPreference();
}
+ rolePreference.setRestrictionIntent(role.getRestrictionIntentAsUser(user, context));
List<ApplicationInfo> holderApplicationInfos = roleItem.getHolderApplicationInfos();
if (holderApplicationInfos.isEmpty()) {
preference.setIcon(null);
@@ -191,7 +239,8 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo));
preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context));
}
- RoleUiBehaviorUtils.preparePreferenceAsUser(role, rolePreference, user, context);
+ RoleUiBehaviorUtils.preparePreferenceAsUser(role, holderApplicationInfos,
+ rolePreference, user, context);
preferenceGroup.addPreference(preference);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
index 06f58ef25..5bc25df54 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
@@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui;
import android.app.Application;
import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -27,6 +28,7 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;
+import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.role.utils.UserUtils;
import java.util.List;
@@ -44,6 +46,10 @@ public class DefaultAppListViewModel extends AndroidViewModel {
private final UserHandle mWorkProfile;
@Nullable
private final LiveData<List<RoleItem>> mWorkLiveData;
+ @Nullable
+ private final UserHandle mPrivateProfile;
+ @Nullable
+ private final LiveData<List<RoleItem>> mPrivateLiveData;
public DefaultAppListViewModel(@NonNull Application application) {
super(application);
@@ -55,6 +61,16 @@ public class DefaultAppListViewModel extends AndroidViewModel {
mWorkProfile = UserUtils.getWorkProfile(application);
mWorkLiveData = mWorkProfile != null ? Transformations.map(new RoleListLiveData(true,
mWorkProfile, application), sortFunction) : null;
+
+ UserHandle privateProfile = UserUtils.getPrivateProfile(application);
+ if (privateProfile != null && Utils.shouldShowInSettings(
+ privateProfile, application.getSystemService(UserManager.class))) {
+ mPrivateProfile = privateProfile;
+ } else {
+ mPrivateProfile = null;
+ }
+ mPrivateLiveData = mPrivateProfile != null ? Transformations.map(new RoleListLiveData(true,
+ mPrivateProfile, application), sortFunction) : null;
}
@NonNull
@@ -85,4 +101,33 @@ public class DefaultAppListViewModel extends AndroidViewModel {
public LiveData<List<RoleItem>> getWorkLiveData() {
return mWorkLiveData;
}
+
+ /**
+ * Check whether the user has a private profile.
+ *
+ * @return whether the user has a private profile.
+ */
+ public boolean hasPrivateProfile() {
+ return mPrivateProfile != null;
+ }
+
+ /**
+ * Returns the private profile belonging to the user, if any.
+ *
+ * @return the private profile, if it exists. null otherwise.
+ */
+ @Nullable
+ public UserHandle getPrivateProfile() {
+ return mPrivateProfile;
+ }
+
+ /**
+ * Returns the data corresponding to the private profile, if one exists.
+ *
+ * @return data corresponding to the private profile, if it exists. null otherwise.
+ */
+ @Nullable
+ public LiveData<List<RoleItem>> getPrivateLiveData() {
+ return mPrivateLiveData;
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
index 827d42643..0c3ea3416 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleActivity.java
@@ -16,13 +16,15 @@
package com.android.permissioncontroller.role.ui;
+import static com.android.permissioncontroller.Constants.EXTRA_IS_ECM_IN_APP;
+
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.Process;
-import android.os.UserManager;
+import android.provider.Settings;
import android.provider.Telephony;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -33,11 +35,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
+import com.android.permissioncontroller.DeviceUtils;
import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.permission.utils.CollectionUtils;
import com.android.permissioncontroller.role.model.UserDeniedManager;
+import com.android.permissioncontroller.role.ui.wear.WearRequestRoleFragment;
import com.android.permissioncontroller.role.utils.PackageUtils;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -103,7 +106,7 @@ public class RequestRoleActivity extends FragmentActivity {
return;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(Process.myUserHandle(), this)) {
Log.e(LOG_TAG, "Role is unavailable: " + mRoleName);
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
@@ -111,7 +114,7 @@ public class RequestRoleActivity extends FragmentActivity {
return;
}
- if (!RoleUiBehaviorUtils.isVisible(role, this)) {
+ if (!role.isVisibleAsUser(Process.myUserHandle(), this)) {
Log.e(LOG_TAG, "Role is invisible: " + mRoleName);
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
@@ -135,7 +138,8 @@ public class RequestRoleActivity extends FragmentActivity {
return;
}
- if (PackageUtils.getApplicationInfo(mPackageName, this) == null) {
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfo(mPackageName, this);
+ if (applicationInfo == null) {
Log.w(LOG_TAG, "Unknown application: " + mPackageName);
reportRequestResult(
PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED);
@@ -155,17 +159,28 @@ 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);
+ Intent restrictionIntent = role.getApplicationRestrictionIntentAsUser(applicationInfo,
+ Process.myUserHandle(), this);
+ if (restrictionIntent != null) {
+ if (Objects.equals(restrictionIntent.getAction(),
+ Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)) {
+ Log.w(LOG_TAG, "Cannot request role due to user restriction"
+ + ", role: " + mRoleName + ", package: " + mPackageName);
+ reportRequestResult(PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_USER_RESTRICTION);
+ } else {
+ Log.w(LOG_TAG, "Cannot request role due to enhanced confirmation restriction"
+ + ", role: " + mRoleName + ", package: " + mPackageName);
+ reportRequestResult(PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ENHANCED_CONFIRMATION_RESTRICTION);
+ restrictionIntent.putExtra(EXTRA_IS_ECM_IN_APP, true);
+ }
+ startActivity(restrictionIntent);
finish();
return;
}
- if (!role.isPackageQualified(mPackageName, this)) {
+ if (!role.isPackageQualifiedAsUser(mPackageName, Process.myUserHandle(), this)) {
Log.w(LOG_TAG, "Application doesn't qualify for role, role: " + mRoleName
+ ", package: " + mPackageName);
reportRequestResult(PermissionControllerStatsLog
@@ -184,10 +199,19 @@ public class RequestRoleActivity extends FragmentActivity {
}
if (savedInstanceState == null) {
- RequestRoleFragment fragment = RequestRoleFragment.newInstance(mRoleName, mPackageName);
- getSupportFragmentManager().beginTransaction()
- .add(fragment, null)
- .commit();
+ if (DeviceUtils.isWear(this)) {
+ WearRequestRoleFragment fragment = WearRequestRoleFragment.newInstance(
+ mRoleName, mPackageName);
+ getSupportFragmentManager().beginTransaction()
+ .add(android.R.id.content, fragment)
+ .commit();
+ } else {
+ RequestRoleFragment fragment = RequestRoleFragment.newInstance(mRoleName,
+ mPackageName);
+ getSupportFragmentManager().beginTransaction()
+ .add(fragment, null)
+ .commit();
+ }
}
}
@@ -302,4 +326,11 @@ public class RequestRoleActivity extends FragmentActivity {
}
return applicationInfo.uid;
}
+
+ @Override
+ protected void onNewIntent(@NonNull Intent intent) {
+ super.onNewIntent(intent);
+
+ Log.w(LOG_TAG, "Ignoring new intent: " + intent);
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
index a5bd90dc2..97ed74c01 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
@@ -35,7 +35,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
@@ -46,7 +45,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.fragment.app.DialogFragment;
-import androidx.lifecycle.ViewModelProviders;
+import androidx.lifecycle.ViewModelProvider;
import com.android.permissioncontroller.PermissionControllerStatsLog;
import com.android.permissioncontroller.R;
@@ -54,6 +53,8 @@ import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.role.model.UserDeniedManager;
import com.android.permissioncontroller.role.utils.PackageUtils;
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
+import com.android.permissioncontroller.role.utils.UiUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -154,7 +155,6 @@ public class RequestRoleFragment extends DialogFragment {
View viewLayout = inflater.inflate(R.layout.request_role_view, null);
mListView = viewLayout.requireViewById(R.id.list);
- mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
mListView.setOnItemClickListener((parent, view, position, id) -> onItemClicked(position));
mAdapter = new Adapter(mListView, mRole);
if (savedInstanceState != null) {
@@ -230,8 +230,9 @@ public class RequestRoleFragment extends DialogFragment {
mPackageRemovalMonitor.register();
// Postponed to onStart() so that the list view in dialog is created.
- mViewModel = ViewModelProviders.of(this, new RequestRoleViewModel.Factory(mRole,
- requireActivity().getApplication())).get(RequestRoleViewModel.class);
+ ViewModelProvider.Factory viewModelFactory = new RequestRoleViewModel.Factory(mRole,
+ requireActivity().getApplication());
+ mViewModel = new ViewModelProvider(this, viewModelFactory).get(RequestRoleViewModel.class);
mViewModel.getRoleLiveData().observe(this, this::onRoleDataChanged);
mViewModel.getManageRoleHolderStateLiveData().observe(this,
this::onManageRoleHolderStateChanged);
@@ -414,9 +415,9 @@ public class RequestRoleFragment extends DialogFragment {
// Skip the "None" item.
continue;
}
- ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first;
- if (Objects.equals(qualifyingApplicationInfo.packageName, packageName)) {
- return qualifyingApplicationInfo.uid;
+ ApplicationInfo applicationInfo = qualifyingApplication.first;
+ if (Objects.equals(applicationInfo.packageName, packageName)) {
+ return applicationInfo.uid;
}
}
return -1;
@@ -439,25 +440,13 @@ public class RequestRoleFragment extends DialogFragment {
if (mAdapter == null) {
return null;
}
- int count = mAdapter.getCount();
- for (int i = 0; i < count; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = mAdapter.getItem(i);
- if (qualifyingApplication == null) {
- // Skip the "None" item.
- continue;
- }
- boolean isHolderApplication = qualifyingApplication.second;
- if (isHolderApplication) {
- return qualifyingApplication.first.packageName;
- }
- }
- return null;
+ return mAdapter.mHolderPackageName;
}
static void reportRequestResult(int requestingUid, String requestingPackageName,
String roleName, int qualifyingCount, int currentUid, String currentPackageName,
int grantedAnotherUid, String grantedAnotherPackageName, int result) {
- Log.v(LOG_TAG, "Role request result"
+ Log.i(LOG_TAG, "Role request result"
+ " requestingUid=" + requestingUid
+ " requestingPackageName=" + requestingPackageName
+ " roleName=" + roleName
@@ -477,8 +466,8 @@ public class RequestRoleFragment extends DialogFragment {
private static final String STATE_USER_CHECKED = Adapter.class.getName()
+ ".state.USER_CHECKED";
- private static final String STATE_USER_CHECKED_PACKAGE_NAME = Adapter.class.getName()
- + ".state.USER_CHECKED_PACKAGE_NAME";
+ private static final String STATE_CHECKED_PACKAGE_NAME = Adapter.class.getName()
+ + ".state.CHECKED_PACKAGE_NAME";
private static final int LAYOUT_TRANSITION_DURATION_MILLIS = 150;
@@ -493,7 +482,8 @@ public class RequestRoleFragment extends DialogFragment {
private final List<Pair<ApplicationInfo, Boolean>> mQualifyingApplications =
new ArrayList<>();
- private boolean mHasHolderApplication;
+ @Nullable
+ private String mHolderPackageName;
private boolean mDontAskAgain;
@@ -501,10 +491,8 @@ public class RequestRoleFragment extends DialogFragment {
// the current holder as checked.
private boolean mUserChecked;
- private boolean mPendingUserChecked;
- // We may use a null to represent the "None" item.
@Nullable
- private String mPendingUserCheckedPackageName;
+ private String mCheckedPackageName;
Adapter(@NonNull ListView listView, @NonNull Role role) {
mListView = listView;
@@ -514,15 +502,14 @@ public class RequestRoleFragment extends DialogFragment {
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(STATE_USER_CHECKED, mUserChecked);
if (mUserChecked) {
- outState.putString(STATE_USER_CHECKED_PACKAGE_NAME, getCheckedPackageName());
+ outState.putString(STATE_CHECKED_PACKAGE_NAME, mCheckedPackageName);
}
}
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
- mPendingUserChecked = savedInstanceState.getBoolean(STATE_USER_CHECKED);
- if (mPendingUserChecked) {
- mPendingUserCheckedPackageName = savedInstanceState.getString(
- STATE_USER_CHECKED_PACKAGE_NAME);
+ mUserChecked = savedInstanceState.getBoolean(STATE_USER_CHECKED);
+ if (mUserChecked) {
+ mCheckedPackageName = savedInstanceState.getString(STATE_CHECKED_PACKAGE_NAME);
}
}
@@ -533,14 +520,28 @@ public class RequestRoleFragment extends DialogFragment {
mDontAskAgain = dontAskAgain;
if (mDontAskAgain) {
mUserChecked = false;
- updateItemChecked();
+ mCheckedPackageName = mHolderPackageName;
}
notifyDataSetChanged();
}
public void onItemClicked(int position) {
- mUserChecked = true;
- // We may need to change description based on checked state.
+ Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
+ if (qualifyingApplication == null) {
+ mUserChecked = true;
+ mCheckedPackageName = null;
+ } else {
+ ApplicationInfo applicationInfo = qualifyingApplication.first;
+ Intent restrictionIntent = mRole.getApplicationRestrictionIntentAsUser(
+ applicationInfo, Process.myUserHandle(), mListView.getContext());
+ if (restrictionIntent != null) {
+ mListView.getContext().startActivity(restrictionIntent);
+ return;
+ } else {
+ mUserChecked = true;
+ mCheckedPackageName = applicationInfo.packageName;
+ }
+ }
notifyDataSetChanged();
}
@@ -550,42 +551,10 @@ public class RequestRoleFragment extends DialogFragment {
mQualifyingApplications.add(0, null);
}
mQualifyingApplications.addAll(qualifyingApplications);
- mHasHolderApplication = hasHolderApplication(qualifyingApplications);
- notifyDataSetChanged();
+ mHolderPackageName = getHolderPackageName(qualifyingApplications);
- if (mPendingUserChecked) {
- restoreItemChecked();
- mPendingUserChecked = false;
- mPendingUserCheckedPackageName = null;
- }
-
- if (!mUserChecked) {
- updateItemChecked();
- }
- }
-
- private static boolean hasHolderApplication(
- @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
- int qualifyingApplicationsSize = qualifyingApplications.size();
- for (int i = 0; i < qualifyingApplicationsSize; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get(
- i);
- boolean isHolderApplication = qualifyingApplication.second;
-
- if (isHolderApplication) {
- return true;
- }
- }
- return false;
- }
-
- private void restoreItemChecked() {
- if (mPendingUserCheckedPackageName == null) {
- if (mRole.shouldShowNone()) {
- mUserChecked = true;
- mListView.setItemChecked(0, true);
- }
- } else {
+ if (mUserChecked && mCheckedPackageName != null) {
+ boolean isCheckedPackageNameFound = false;
int count = getCount();
for (int i = 0; i < count; i++) {
Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(i);
@@ -594,55 +563,52 @@ public class RequestRoleFragment extends DialogFragment {
}
String packageName = qualifyingApplication.first.packageName;
- if (Objects.equals(packageName, mPendingUserCheckedPackageName)) {
+ if (Objects.equals(packageName, mCheckedPackageName)) {
mUserChecked = true;
- mListView.setItemChecked(i, true);
+ isCheckedPackageNameFound = true;
break;
}
}
+ if (!isCheckedPackageNameFound) {
+ mUserChecked = false;
+ mCheckedPackageName = null;
+ }
+ }
+
+ if (!mUserChecked) {
+ mCheckedPackageName = mHolderPackageName;
}
+
+ notifyDataSetChanged();
}
- private void updateItemChecked() {
- if (!mHasHolderApplication) {
- if (mRole.shouldShowNone()) {
- mListView.setItemChecked(0, true);
- } else {
- mListView.clearChoices();
+ @Nullable
+ private static String getHolderPackageName(
+ @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
+ int qualifyingApplicationSize = qualifyingApplications.size();
+ for (int i = 0; i < qualifyingApplicationSize; i++) {
+ Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get(
+ i);
+ if (qualifyingApplication == null) {
+ continue;
}
- } else {
- int count = getCount();
- for (int i = 0; i < count; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(i);
- if (qualifyingApplication == null) {
- continue;
- }
- boolean isHolderApplication = qualifyingApplication.second;
+ ApplicationInfo applicationInfo = qualifyingApplication.first;
+ boolean isHolderApplication = qualifyingApplication.second;
- if (isHolderApplication) {
- mListView.setItemChecked(i, true);
- break;
- }
+ if (isHolderApplication) {
+ return applicationInfo.packageName;
}
}
- }
-
- @Nullable
- public Pair<ApplicationInfo, Boolean> getCheckedItem() {
- int position = mListView.getCheckedItemPosition();
- return position != AdapterView.INVALID_POSITION ? getItem(position) : null;
+ return null;
}
@Nullable
public String getCheckedPackageName() {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getCheckedItem();
- return qualifyingApplication == null ? null : qualifyingApplication.first.packageName;
+ return mCheckedPackageName;
}
public boolean isHolderApplicationChecked() {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getCheckedItem();
- return qualifyingApplication == null ? !mHasHolderApplication
- : qualifyingApplication.second;
+ return Objects.equals(mCheckedPackageName, mHolderPackageName);
}
@Override
@@ -684,7 +650,7 @@ public class RequestRoleFragment extends DialogFragment {
}
Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
if (qualifyingApplication == null) {
- return !mHasHolderApplication;
+ return mHolderPackageName == null;
} else {
boolean isHolderApplication = qualifyingApplication.second;
return isHolderApplication;
@@ -695,13 +661,13 @@ public class RequestRoleFragment extends DialogFragment {
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
Context context = parent.getContext();
- View view = convertView;
+ CheckableLinearLayout view = (CheckableLinearLayout) convertView;
ViewHolder holder;
if (view != null) {
holder = (ViewHolder) view.getTag();
} else {
- view = LayoutInflater.from(context).inflate(R.layout.request_role_item, parent,
- false);
+ view = (CheckableLinearLayout) LayoutInflater.from(context).inflate(
+ R.layout.request_role_item, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
@@ -709,38 +675,50 @@ public class RequestRoleFragment extends DialogFragment {
LAYOUT_TRANSITION_DURATION_MILLIS);
}
- view.setEnabled(isEnabled(position));
-
Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
+ ApplicationInfo applicationInfo;
+ boolean restricted;
+ boolean checked;
Drawable icon;
String title;
String subtitle;
if (qualifyingApplication == null) {
+ applicationInfo = null;
+ restricted = false;
+ checked = mCheckedPackageName == null;
icon = AppCompatResources.getDrawable(context, R.drawable.ic_remove_circle);
title = context.getString(R.string.default_app_none);
- subtitle = !mHasHolderApplication ? context.getString(
+ subtitle = mHolderPackageName != null ? context.getString(
R.string.request_role_current_default) : null;
} else {
- ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first;
- icon = Utils.getBadgedIcon(context, qualifyingApplicationInfo);
- title = Utils.getAppLabel(qualifyingApplicationInfo, context);
+ applicationInfo = qualifyingApplication.first;
+ restricted = mRole.getApplicationRestrictionIntentAsUser(applicationInfo,
+ Process.myUserHandle(), context) != null;
+ checked = Objects.equals(applicationInfo.packageName, mCheckedPackageName);
+ icon = Utils.getBadgedIcon(context, applicationInfo);
+ title = Utils.getAppLabel(applicationInfo, context);
boolean isHolderApplication = qualifyingApplication.second;
subtitle = isHolderApplication
? context.getString(R.string.request_role_current_default)
- : mListView.isItemChecked(position)
- ? context.getString(mRole.getRequestDescriptionResource()) : null;
+ : checked ? context.getString(mRole.getRequestDescriptionResource()) : null;
}
+ boolean enabled = isEnabled(position);
+ UiUtils.setViewTreeEnabled(view, enabled && !restricted);
+ view.setEnabled(enabled);
+ view.setChecked(checked);
holder.iconImage.setImageDrawable(icon);
holder.titleText.setText(title);
holder.subtitleText.setVisibility(!TextUtils.isEmpty(subtitle) ? View.VISIBLE
: View.GONE);
holder.subtitleText.setText(subtitle);
+ RoleUiBehaviorUtils.prepareRequestRoleItemViewAsUser(mRole, holder, applicationInfo,
+ Process.myUserHandle(), context);
return view;
}
- private static class ViewHolder {
+ private static class ViewHolder implements RequestRoleItemView {
@NonNull
public final ImageView iconImage;
@@ -757,6 +735,21 @@ public class RequestRoleFragment extends DialogFragment {
titleText = view.requireViewById(R.id.title);
subtitleText = view.requireViewById(R.id.subtitle);
}
+
+ @Override
+ public ImageView getIconImageView() {
+ return iconImage;
+ }
+
+ @Override
+ public TextView getTitleTextView() {
+ return titleText;
+ }
+
+ @Override
+ public TextView getSubtitleTextView() {
+ return subtitleText;
+ }
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleItemView.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleItemView.java
new file mode 100644
index 000000000..25dea89ad
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleItemView.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.role.ui;
+
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Item view for qualifying applications in role requests.
+ */
+public interface RequestRoleItemView {
+
+ /**
+ * Get the {@link ImageView} for item icon.
+ */
+ @NonNull
+ ImageView getIconImageView();
+
+ /**
+ * Get the {@link TextView} for item title.
+ */
+ @NonNull
+ TextView getTitleTextView();
+
+ /**
+ * Get the {@link TextView} for item subtitle.
+ */
+ @NonNull
+ TextView getSubtitleTextView();
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RestrictionAwarePreference.java
index e6bc9bab6..f68253161 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RestrictionAwarePreference.java
@@ -16,15 +16,17 @@
package com.android.permissioncontroller.role.ui;
+import android.content.Intent;
+
import androidx.annotation.Nullable;
/**
- * Preference that is aware of user restrictions that can block them.
+ * Preference that is aware of restrictions that can block them.
*/
-public interface UserRestrictionAwarePreference {
+public interface RestrictionAwarePreference {
/**
- * Specifies user restriction that blocks this preference.
+ * Set the restriction intent that blocks this preference.
*/
- void setUserRestriction(@Nullable String userRestriction);
+ void setRestrictionIntent(@Nullable Intent restrictionIntent);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RestrictionAwarePreferenceMixin.java
index 033507991..8d757324f 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/UserRestrictionAwarePreferenceMixin.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RestrictionAwarePreferenceMixin.java
@@ -16,9 +16,7 @@
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;
@@ -26,32 +24,35 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
/**
- * Mixin for implementing {@link UserRestrictionAwarePreference}.
+ * Mixin for implementing {@link RestrictionAwarePreference}.
*/
-public class UserRestrictionAwarePreferenceMixin {
+public class RestrictionAwarePreferenceMixin {
+
+ private static final String LOG_TAG = RestrictionAwarePreferenceMixin.class.getSimpleName();
@NonNull
private final Preference mPreference;
+
@Nullable
- private String mUserRestriction = null;
+ private Intent mRestrictionIntent;
- public UserRestrictionAwarePreferenceMixin(@NonNull Preference preference) {
+ public RestrictionAwarePreferenceMixin(@NonNull Preference preference) {
mPreference = preference;
}
/**
- * Implementation for {@link UserRestrictionAwarePreference#setUserRestriction}.
+ * Implementation for {@link RestrictionAwarePreference#setRestrictionIntent}.
*/
- public void setUserRestriction(@Nullable String userRestriction) {
- mUserRestriction = userRestriction;
- mPreference.setEnabled(mUserRestriction == null);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionIntent = restrictionIntent;
+ mPreference.setEnabled(mRestrictionIntent == null);
}
/**
* Call after {@link Preference#onBindViewHolder} to apply blocking effects.
*/
public void onAfterBindViewHolder(@NonNull PreferenceViewHolder holder) {
- if (mUserRestriction != null) {
+ if (mRestrictionIntent != 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
@@ -60,10 +61,8 @@ public class UserRestrictionAwarePreferenceMixin {
// 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));
+ view -> view.getContext().startActivity(mRestrictionIntent));
}
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java
index 1b5b27971..1d3e32c9c 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationPreference.java
@@ -22,7 +22,7 @@ import androidx.preference.TwoStatePreference;
/**
* Preference for application being a candidate or holding a role.
*/
-public interface RoleApplicationPreference extends UserRestrictionAwarePreference {
+public interface RoleApplicationPreference extends RestrictionAwarePreference {
/**
* Get instance of {@code this} as {@link TwoStatePreference}.
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java
index b9011bd78..e6df3ed8a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListLiveData.java
@@ -30,7 +30,6 @@ import androidx.lifecycle.LiveData;
import com.android.permissioncontroller.AsyncTaskLiveData;
import com.android.permissioncontroller.role.utils.PackageUtils;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -97,7 +96,7 @@ public class RoleListLiveData extends AsyncTaskLiveData<List<RoleItem>>
continue;
}
- if (!RoleUiBehaviorUtils.isVisibleAsUser(role, mUser, mContext)) {
+ if (!role.isVisibleAsUser(mUser, mContext)) {
continue;
}
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/RoleLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
index 3ccb1d8bc..bb492f76d 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
@@ -30,7 +30,6 @@ import androidx.lifecycle.LiveData;
import com.android.permissioncontroller.AsyncTaskLiveData;
import com.android.permissioncontroller.role.utils.PackageUtils;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import java.util.ArrayList;
@@ -95,8 +94,7 @@ public class RoleLiveData extends AsyncTaskLiveData<List<Pair<ApplicationInfo, B
+ qualifyingPackageName);
continue;
}
- if (!RoleUiBehaviorUtils.isApplicationVisibleAsUser(mRole, qualifyingApplicationInfo,
- mUser, mContext)) {
+ if (!mRole.isApplicationVisibleAsUser(qualifyingApplicationInfo, mUser, mContext)) {
continue;
}
boolean isHolderApplication = holderPackageNames.contains(qualifyingPackageName);
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java
index 442963ce6..bbc123cfe 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RolePreference.java
@@ -16,14 +16,16 @@
package com.android.permissioncontroller.role.ui;
+import androidx.annotation.NonNull;
import androidx.preference.Preference;
/**
* Preference used by the default apps list UI.
*/
-public interface RolePreference extends TwoTargetPreference, UserRestrictionAwarePreference {
+public interface RolePreference extends TwoTargetPreference, RestrictionAwarePreference {
/**
* Return this preference as {@link Preference}.
*/
+ @NonNull
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 3a8cd55d3..ab2387686 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/TwoTargetPreference.java
@@ -41,6 +41,7 @@ public interface TwoTargetPreference {
/**
* Return this preference as {@link Preference}.
*/
+ @NonNull
Preference asPreference();
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java
index 97a3dad26..764c07497 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRadioPreference.java
@@ -17,27 +17,30 @@
package com.android.permissioncontroller.role.ui.auto;
import android.content.Context;
+import android.content.Intent;
import android.widget.RadioButton;
+import androidx.annotation.NonNull;
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.RestrictionAwarePreferenceMixin;
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 AutoRadioPreference extends TwoStatePreference implements
RoleApplicationPreference {
- private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
- new UserRestrictionAwarePreferenceMixin(this);
+ private final RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin =
+ new RestrictionAwarePreferenceMixin(this);
- public AutoRadioPreference(Context context) {
- super(context, null, TypedArrayUtils.getAttr(context, R.attr.preferenceStyle,
- android.R.attr.preferenceStyle));
+ public AutoRadioPreference(@NonNull Context context) {
+ super(context, null,
+ TypedArrayUtils.getAttr(context, androidx.preference.R.attr.preferenceStyle,
+ android.R.attr.preferenceStyle));
init();
}
@@ -47,20 +50,21 @@ public class AutoRadioPreference extends TwoStatePreference implements
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
RadioButton radioButton = (RadioButton) holder.findViewById(R.id.radio_button);
radioButton.setChecked(isChecked());
- mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
}
@Override
- public void setUserRestriction(@Nullable String userRestriction) {
- mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent);
}
+ @NonNull
@Override
public AutoRadioPreference asTwoStatePreference() {
return this;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java
index d2f1b6cde..15fd117d1 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoRolePreference.java
@@ -17,16 +17,19 @@
package com.android.permissioncontroller.role.ui.auto;
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
+import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin;
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
@@ -34,16 +37,16 @@ import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMi
*/
public class AutoRolePreference extends Preference implements RolePreference {
- private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
- new UserRestrictionAwarePreferenceMixin(this);
+ private RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin =
+ new RestrictionAwarePreferenceMixin(this);
public AutoRolePreference(@NonNull Context context,
- @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public AutoRolePreference(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
+ @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@@ -56,21 +59,21 @@ public class AutoRolePreference extends Preference implements RolePreference {
}
@Override
- public void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener) {
- }
+ public void setOnSecondTargetClickListener(@Nullable OnSecondTargetClickListener listener) {}
@Override
- public void setUserRestriction(@Nullable String userRestriction) {
- mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent);
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
}
+ @NonNull
@Override
public AutoRolePreference asPreference() {
return this;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java
index 900e58551..bfb2b5d1d 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/auto/AutoSwitchPreference.java
@@ -17,6 +17,7 @@
package com.android.permissioncontroller.role.ui.auto;
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
import androidx.annotation.AttrRes;
@@ -26,8 +27,8 @@ import androidx.annotation.StyleRes;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
+import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin;
import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
-import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
/**
* Role application preference represented as a switch.
@@ -35,8 +36,8 @@ import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMi
public class AutoSwitchPreference extends SwitchPreference
implements RoleApplicationPreference {
- private UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
- new UserRestrictionAwarePreferenceMixin(this);
+ private RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin =
+ new RestrictionAwarePreferenceMixin(this);
public AutoSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
@@ -57,15 +58,15 @@ public class AutoSwitchPreference extends SwitchPreference
}
@Override
- public void setUserRestriction(@Nullable String userRestriction) {
- mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent);
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java
index 40bd7a33e..4df3ccf99 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/AssistantRoleUiBehavior.java
@@ -26,7 +26,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.permissioncontroller.R;
-import com.android.permissioncontroller.role.model.VisibilityMixin;
import com.android.role.controller.model.Role;
/***
@@ -34,12 +33,6 @@ import com.android.role.controller.model.Role;
*/
public class AssistantRoleUiBehavior implements RoleUiBehavior {
- @Override
- public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return VisibilityMixin.isVisible("config_showDefaultAssistant", context);
- }
-
@Nullable
@Override
public Intent getManageIntentAsUser(@NonNull Role role, @NonNull UserHandle user,
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 e6b8dabe1..ab87e24cf 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/DialerRoleUiBehavior.java
@@ -49,12 +49,6 @@ public class DialerRoleUiBehavior implements RoleUiBehavior {
}
}
- @Override
- public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return context.getResources().getBoolean(R.bool.config_showDialerRole);
- }
-
@Nullable
@Override
public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java
index 8a62ee7eb..f891bb3ed 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/EmergencyRoleUiBehavior.java
@@ -17,13 +17,11 @@
package com.android.permissioncontroller.role.ui.behavior;
import android.content.Context;
-import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin;
-import com.android.permissioncontroller.role.model.VisibilityMixin;
import com.android.role.controller.model.Role;
/***
@@ -31,12 +29,6 @@ import com.android.role.controller.model.Role;
*/
public class EmergencyRoleUiBehavior implements RoleUiBehavior {
- @Override
- public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return VisibilityMixin.isVisible("config_showDefaultEmergency", context);
- }
-
@Nullable
@Override
public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
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 d0e7c0eef..0142e1c40 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/HomeRoleUiBehavior.java
@@ -34,12 +34,12 @@ import androidx.preference.Preference;
import com.android.permissioncontroller.R;
import com.android.permissioncontroller.permission.utils.CollectionUtils;
import com.android.permissioncontroller.permission.utils.Utils;
-import com.android.permissioncontroller.role.model.VisibilityMixin;
import com.android.permissioncontroller.role.ui.TwoTargetPreference;
import com.android.permissioncontroller.role.utils.UserUtils;
-import com.android.role.controller.behavior.HomeRoleBehavior;
import com.android.role.controller.model.Role;
+import java.util.List;
+
/***
* Class for UI behavior of Home role
*/
@@ -48,14 +48,9 @@ public class HomeRoleUiBehavior implements RoleUiBehavior {
private static final String LOG_TAG = HomeRoleUiBehavior.class.getSimpleName();
@Override
- public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return VisibilityMixin.isVisible("config_showDefaultHome", context);
- }
-
- @Override
public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
- @NonNull UserHandle user, @NonNull Context context) {
+ @NonNull List<ApplicationInfo> applicationInfos, @NonNull UserHandle user,
+ @NonNull Context context) {
TwoTargetPreference.OnSecondTargetClickListener listener = null;
RoleManager roleManager = context.getSystemService(RoleManager.class);
String packageName = CollectionUtils.firstOrNull(roleManager.getRoleHoldersAsUser(
@@ -82,14 +77,6 @@ public class HomeRoleUiBehavior implements RoleUiBehavior {
}
@Override
- public boolean isApplicationVisibleAsUser(@NonNull Role role,
- @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
- @NonNull Context context) {
- // Home is not available for work profile, so we can just use the current user.
- return !HomeRoleBehavior.isSettingsApplication(applicationInfo, context);
- }
-
- @Override
public void prepareApplicationPreferenceAsUser(@NonNull Role role,
@NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
@NonNull UserHandle user, @NonNull Context context) {
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 6e3b47fba..ae5c03676 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/RoleUiBehavior.java
@@ -25,27 +25,29 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
+import com.android.permissioncontroller.role.ui.RequestRoleItemView;
import com.android.permissioncontroller.role.ui.TwoTargetPreference;
import com.android.role.controller.model.Role;
+import java.util.List;
+
/***
* Interface for UI behavior for roles
*/
public interface RoleUiBehavior {
/**
- * Check whether this role should be visible to user.
- *
- * @param role the role to check for
- * @param user the user to check for
- * @param context the `Context` to retrieve system services
+ * Prepare a {@link RequestRoleItemView} for this role and an application.
*
- * @return whether this role should be visible to user
+ * @param role the role to prepare the preference for
+ * @param itemView the {@link RequestRoleItemView} for the application
+ * @param applicationInfo the {@link ApplicationInfo} for the application
+ * @param user the user for this role
+ * @param context the {@code Context} to retrieve system services
*/
- default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return true;
- }
+ default void prepareRequestRoleItemViewAsUser(@NonNull Role role,
+ @NonNull RequestRoleItemView itemView, @NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {}
/**
* Get the {@link Intent} to manage this role, or {@code null} to use the default UI.
@@ -67,34 +69,21 @@ public interface RoleUiBehavior {
*
* @param role the role to prepare the preference for
* @param preference the {@link Preference} for this role
+ * @param applicationInfos a list {@link ApplicationInfo} for the current role holders
* @param user the user for this role
* @param context the {@code Context} to retrieve system services
*/
default void preparePreferenceAsUser(@NonNull Role role,
@NonNull TwoTargetPreference preference,
- @NonNull UserHandle user,
- @NonNull Context context) {}
-
- /**
- * Check whether a qualifying application should be visible to user.
- *
- * @param applicationInfo the {@link ApplicationInfo} for the application
- * @param user the user for the application
- * @param context the {@code Context} to retrieve system services
- *
- * @return whether the qualifying application should be visible to user
- */
- default boolean isApplicationVisibleAsUser(@NonNull Role role,
- @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
- @NonNull Context context) {
- return true;
- }
+ @NonNull List<ApplicationInfo> applicationInfos,
+ @NonNull UserHandle user, @NonNull Context context) {}
/**
- * Prepare a {@link Preference} for this role.
+ * Prepare a {@link Preference} for this role and an application.
*
* @param role the role to prepare the preference for
- * @param preference the {@link Preference} for this role
+ * @param preference the {@link Preference} for the application
+ * @param applicationInfo the {@link ApplicationInfo} for the application
* @param user the user for this role
* @param context the {@code Context} to retrieve system services
*/
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java
index 9fc9be3d4..e27bc1a30 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/SmsRoleUiBehavior.java
@@ -17,12 +17,10 @@
package com.android.permissioncontroller.role.ui.behavior;
import android.content.Context;
-import android.os.UserHandle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.permissioncontroller.R;
import com.android.permissioncontroller.role.model.EncryptionUnawareConfirmationMixin;
import com.android.role.controller.model.Role;
@@ -31,12 +29,6 @@ import com.android.role.controller.model.Role;
*/
public class SmsRoleUiBehavior implements RoleUiBehavior {
- @Override
- public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
- return context.getResources().getBoolean(R.bool.config_showSmsRole);
- }
-
@Nullable
@Override
public CharSequence getConfirmationMessage(@NonNull Role role, @NonNull String packageName,
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java
new file mode 100644
index 000000000..aa6194997
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/behavior/v35/WalletRoleUiBehavior.java
@@ -0,0 +1,190 @@
+/*
+ * 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.behavior.v35;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.nfc.cardemulation.ApduServiceInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.nfc.cardemulation.HostApduService;
+import android.nfc.cardemulation.OffHostApduService;
+import android.os.Build;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.util.Pair;
+import androidx.preference.Preference;
+
+import com.android.permissioncontroller.role.ui.TwoTargetPreference;
+import com.android.permissioncontroller.role.ui.behavior.RoleUiBehavior;
+import com.android.role.controller.model.Role;
+import com.android.role.controller.util.UserUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/***
+ * Class for UI behavior of Wallet role
+ */
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+public class WalletRoleUiBehavior implements RoleUiBehavior {
+
+ private static final String LOG_TAG = WalletRoleUiBehavior.class.getSimpleName();
+
+ @Override
+ public void preparePreferenceAsUser(@NonNull Role role, @NonNull TwoTargetPreference preference,
+ @NonNull List<ApplicationInfo> applicationInfos, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ if (!applicationInfos.isEmpty()) {
+ preparePreferenceInternal(preference.asPreference(), applicationInfos.get(0),
+ false, user, userContext);
+ }
+ }
+
+ @Override
+ public void prepareApplicationPreferenceAsUser(@NonNull Role role,
+ @NonNull Preference preference, @NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ preparePreferenceInternal(preference, applicationInfo, true, user, userContext);
+ }
+
+ private void preparePreferenceInternal(@NonNull Preference preference,
+ @NonNull ApplicationInfo applicationInfo, boolean setTitle, @NonNull UserHandle user,
+ @NonNull Context context) {
+ if (isSystemApplication(applicationInfo)) {
+ List<ApduServiceInfo> serviceInfos = getNfcServicesForPackage(
+ applicationInfo.packageName, user, context);
+
+ Pair<Drawable, CharSequence> bannerAndLabel =
+ getNonPaymentServiceBannerAndLabelIfExists(serviceInfos, user, context);
+ if (bannerAndLabel != null) {
+ preference.setIcon(bannerAndLabel.first);
+ if (setTitle) {
+ preference.setTitle(bannerAndLabel.second);
+ } else {
+ preference.setSummary(bannerAndLabel.second);
+ }
+ }
+ }
+ }
+
+ @NonNull
+ private static List<ApduServiceInfo> getNfcServicesForPackage(@NonNull String packageName,
+ @NonNull UserHandle user, @NonNull Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ Intent hostApduIntent = new Intent(HostApduService.SERVICE_INTERFACE);
+ Intent offHostApduIntent = new Intent(OffHostApduService.SERVICE_INTERFACE);
+ hostApduIntent.setPackage(packageName);
+ offHostApduIntent.setPackage(packageName);
+ List<ResolveInfo> hostApduServices = packageManager.queryIntentServicesAsUser(
+ hostApduIntent,
+ PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DISABLED_COMPONENTS), user);
+ List<ResolveInfo> offHostApduServices = packageManager.queryIntentServicesAsUser(
+ offHostApduIntent,
+ PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA
+ | PackageManager.MATCH_DISABLED_COMPONENTS), user);
+ List<ApduServiceInfo> nfcServices = new ArrayList<>();
+ int apduServiceInfoSize = hostApduServices.size();
+ for (int i = 0; i < apduServiceInfoSize; i++) {
+ ResolveInfo resolveInfo = hostApduServices.get(i);
+ ApduServiceInfo apduServiceInfo;
+ try {
+ apduServiceInfo = new ApduServiceInfo(packageManager, resolveInfo, true);
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(LOG_TAG, "Error creating the apduserviceinfo.", e);
+ continue;
+ }
+ nfcServices.add(apduServiceInfo);
+ }
+ int offHostApduServiceInfoSize = offHostApduServices.size();
+ for (int i = 0; i < offHostApduServiceInfoSize; i++) {
+ ResolveInfo resolveInfo = offHostApduServices.get(i);
+ ApduServiceInfo apduServiceInfo;
+ try {
+ apduServiceInfo = new ApduServiceInfo(packageManager, resolveInfo, false);
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(LOG_TAG, "Error creating the apduserviceinfo.", e);
+ continue;
+ }
+ nfcServices.add(apduServiceInfo);
+ }
+ return nfcServices;
+ }
+
+ @Nullable
+ private Pair<Drawable, CharSequence> getNonPaymentServiceBannerAndLabelIfExists(
+ @NonNull List<ApduServiceInfo> apduServiceInfos, @NonNull UserHandle user,
+ @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ Pair<Drawable, CharSequence> bannerAndLabel;
+ int apduServiceInfoSize = apduServiceInfos.size();
+ for (int i = 0; i < apduServiceInfoSize; i++) {
+ ApduServiceInfo serviceInfo = apduServiceInfos.get(i);
+ if (serviceInfo.getAids().isEmpty()) {
+ bannerAndLabel = loadBannerAndLabel(serviceInfo, userPackageManager);
+ if (bannerAndLabel != null) {
+ return bannerAndLabel;
+ }
+ } else {
+ List<String> aids = serviceInfo.getAids();
+ int aidsSize = aids.size();
+ for (int j = 0; j < aidsSize; j++) {
+ String aid = aids.get(j);
+ String category = serviceInfo.getCategoryForAid(aid);
+ if (!CardEmulation.CATEGORY_PAYMENT.equals(category)) {
+ bannerAndLabel = loadBannerAndLabel(serviceInfo, userPackageManager);
+ if (bannerAndLabel != null) {
+ return bannerAndLabel;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private Pair<Drawable, CharSequence> loadBannerAndLabel(@NonNull ApduServiceInfo info,
+ @NonNull PackageManager userPackageManager) {
+ Drawable drawable = info.loadBanner(userPackageManager);
+ CharSequence label = info.loadLabel(userPackageManager);
+ if (drawable != null && !TextUtils.isEmpty(label)) {
+ return new Pair<>(drawable, label);
+ } else {
+ return null;
+ }
+ }
+
+ private static boolean isSystemApplication(@NonNull ApplicationInfo applicationInfo) {
+ return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java
index d9ef047d6..67f04051c 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRadioPreference.java
@@ -17,14 +17,15 @@
package com.android.permissioncontroller.role.ui.handheld;
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceViewHolder;
+import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin;
import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
-import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
/**
@@ -33,8 +34,8 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference;
public class HandheldRadioPreference extends SelectorWithWidgetPreference implements
RoleApplicationPreference {
- private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
- new UserRestrictionAwarePreferenceMixin(this);
+ private final RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin =
+ new RestrictionAwarePreferenceMixin(this);
public HandheldRadioPreference(@NonNull Context context,
@Nullable AttributeSet attrs, int defStyle) {
@@ -55,16 +56,15 @@ public class HandheldRadioPreference extends SelectorWithWidgetPreference implem
}
@Override
- public void setUserRestriction(
- @Nullable String userRestriction) {
- mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent);
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java
index 978fe7d5a..3d09f0b46 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/handheld/HandheldRolePreference.java
@@ -17,6 +17,7 @@
package com.android.permissioncontroller.role.ui.handheld;
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
import android.view.View;
@@ -28,8 +29,8 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin;
import com.android.permissioncontroller.role.ui.RolePreference;
-import com.android.permissioncontroller.role.ui.UserRestrictionAwarePreferenceMixin;
import com.android.settingslib.widget.TwoTargetPreference;
/**
@@ -40,8 +41,8 @@ import com.android.settingslib.widget.TwoTargetPreference;
// Made public for com.android.permissioncontroller.role.ui.specialappaccess.handheld
public class HandheldRolePreference extends TwoTargetPreference implements RolePreference {
- private final UserRestrictionAwarePreferenceMixin mUserRestrictionAwarePreferenceMixin =
- new UserRestrictionAwarePreferenceMixin(this);
+ private final RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin =
+ new RestrictionAwarePreferenceMixin(this);
@Nullable
private OnSecondTargetClickListener mOnSecondTargetClickListener;
@@ -93,8 +94,8 @@ public class HandheldRolePreference extends TwoTargetPreference implements RoleP
}
@Override
- public void setUserRestriction(@Nullable String userRestriction) {
- mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent);
}
@Override
@@ -113,9 +114,10 @@ public class HandheldRolePreference extends TwoTargetPreference implements RoleP
// Make the settings button enabled even if the preference itself is disabled.
settingsButton.setEnabled(true);
- mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
}
+ @NonNull
@Override
public HandheldRolePreference asPreference() {
return this;
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java
index 6ed105149..472464061 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessActivity.java
@@ -19,6 +19,7 @@ package com.android.permissioncontroller.role.ui.specialappaccess;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -30,7 +31,6 @@ import com.android.permissioncontroller.R;
import com.android.permissioncontroller.role.ui.SettingsActivity;
import com.android.permissioncontroller.role.ui.auto.AutoSpecialAppAccessFragment;
import com.android.permissioncontroller.role.ui.specialappaccess.handheld.HandheldSpecialAppAccessFragment;
-import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
import com.android.role.controller.model.Roles;
@@ -71,13 +71,13 @@ public class SpecialAppAccessActivity extends SettingsActivity {
finish();
return;
}
- if (!role.isAvailable(this)) {
+ if (!role.isAvailableAsUser(Process.myUserHandle(), this)) {
Log.e(LOG_TAG, "Role is unavailable: " + roleName);
finish();
return;
}
- if (!RoleUiBehaviorUtils.isVisible(role, this)) {
+ if (!role.isVisibleAsUser(Process.myUserHandle(), this)) {
Log.e(LOG_TAG, "Role is invisible: " + roleName);
finish();
return;
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 b95440bbd..0963635e7 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
@@ -28,7 +28,7 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProviders;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
@@ -103,8 +103,10 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
mRole = Roles.get(activity).get(mRoleName);
preferenceFragment.setTitle(getString(mRole.getLabelResource()));
- mViewModel = ViewModelProviders.of(this, new SpecialAppAccessViewModel.Factory(mRole,
- activity.getApplication())).get(SpecialAppAccessViewModel.class);
+ ViewModelProvider.Factory viewModelFactory = new SpecialAppAccessViewModel.Factory(mRole,
+ activity.getApplication());
+ mViewModel = new ViewModelProvider(this, viewModelFactory)
+ .get(SpecialAppAccessViewModel.class);
mViewModel.getRoleLiveData().observe(this, this::onRoleChanged);
mViewModel.observeManageRoleHolderState(this, this::onManageRoleHolderStateChanged);
}
@@ -164,6 +166,9 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
preference.setChecked(isHolderPackage);
UserHandle user = UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid);
+ roleApplicationPreference.setRestrictionIntent(
+ mRole.getApplicationRestrictionIntentAsUser(qualifyingApplicationInfo, user,
+ context));
RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference,
qualifyingApplicationInfo, user, context);
preferenceScreen.addPreference(preference);
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 4b256cef0..22169abf2 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessListChildFragment.java
@@ -26,7 +26,7 @@ import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProviders;
+import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
@@ -69,7 +69,7 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mViewModel = ViewModelProviders.of(this).get(SpecialAppAccessListViewModel.class);
+ mViewModel = new ViewModelProvider(this).get(SpecialAppAccessListViewModel.class);
mViewModel.getLiveData().observe(this, roleItems -> onRoleListChanged());
}
@@ -115,9 +115,11 @@ public class SpecialAppAccessListChildFragment<PF extends PreferenceFragmentComp
} else {
preference = rolePreference.asPreference();
}
- RoleUiBehaviorUtils.preparePreferenceAsUser(role, rolePreference,
- Process.myUserHandle(),
- context);
+
+ rolePreference.setRestrictionIntent(role.getRestrictionIntentAsUser(
+ Process.myUserHandle(), context));
+ RoleUiBehaviorUtils.preparePreferenceAsUser(role, roleItem.getHolderApplicationInfos(),
+ rolePreference, Process.myUserHandle(), context);
preferenceScreen.addPreference(preference);
}
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
index 1b4dd78a4..ded6d5cb5 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/handheld/HandheldSwitchPreference.java
@@ -17,6 +17,7 @@
package com.android.permissioncontroller.role.ui.specialappaccess.handheld;
import android.content.Context;
+import android.content.Intent;
import android.util.AttributeSet;
import androidx.annotation.AttrRes;
@@ -25,16 +26,16 @@ import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.preference.PreferenceViewHolder;
+import com.android.permissioncontroller.role.ui.RestrictionAwarePreferenceMixin;
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);
+ private RestrictionAwarePreferenceMixin mRestrictionAwarePreferenceMixin =
+ new RestrictionAwarePreferenceMixin(this);
public HandheldSwitchPreference(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
@@ -55,15 +56,15 @@ public class HandheldSwitchPreference extends AppSwitchPreference
}
@Override
- public void setUserRestriction(@Nullable String userRestriction) {
- mUserRestrictionAwarePreferenceMixin.setUserRestriction(userRestriction);
+ public void setRestrictionIntent(@Nullable Intent restrictionIntent) {
+ mRestrictionAwarePreferenceMixin.setRestrictionIntent(restrictionIntent);
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- mUserRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
+ mRestrictionAwarePreferenceMixin.onAfterBindViewHolder(holder);
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/v35/ChangeDefaultCardEmulationActivity.java b/PermissionController/src/com/android/permissioncontroller/role/ui/v35/ChangeDefaultCardEmulationActivity.java
new file mode 100644
index 000000000..e1f92e2d3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/v35/ChangeDefaultCardEmulationActivity.java
@@ -0,0 +1,77 @@
+/*
+ * 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.v35;
+
+import android.app.Activity;
+import android.app.role.RoleManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+import android.os.Process;
+import android.permission.flags.Flags;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.role.ui.DefaultAppActivity;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Activity to handle {@link android.nfc.cardemulation.CardEmulation#ACTION_CHANGE_DEFAULT}.
+ */
+public class ChangeDefaultCardEmulationActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent;
+ if (SdkLevel.isAtLeastV() && Flags.walletRoleEnabled()) {
+ intent = DefaultAppActivity.createIntent(RoleManager.ROLE_WALLET,
+ Process.myUserHandle(), this);
+ } else {
+ intent = getIntent();
+ setDefaultPaymentChangeHandlerDialogComponent(intent);
+ }
+ intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(intent);
+ finish();
+ }
+
+ // The only other handler of this intent is in the NFC stack.
+ private void setDefaultPaymentChangeHandlerDialogComponent(@NonNull Intent intent) {
+ Intent queryIntent = new Intent(CardEmulation.ACTION_CHANGE_DEFAULT);
+ PackageManager packageManager = getPackageManager();
+ List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(queryIntent,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ int resolveInfosSize = resolveInfos.size();
+ for (int i = 0; i < resolveInfosSize; i++) {
+ ResolveInfo resolveInfo = resolveInfos.get(i);
+ String packageName = resolveInfo.activityInfo.packageName;
+ if (!Objects.equals(packageName, getPackageName())) {
+ intent.setClassName(packageName,
+ resolveInfo.activityInfo.name);
+ return;
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppFragment.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppFragment.kt
new file mode 100644
index 000000000..156656e33
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppFragment.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.wear
+
+import android.content.Intent
+import android.os.Bundle
+import android.os.UserHandle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.core.os.BundleCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme
+import com.android.permissioncontroller.role.ui.DefaultAppViewModel
+import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
+import com.android.permissioncontroller.role.ui.wear.model.DefaultAppConfirmDialogViewModel
+import com.android.permissioncontroller.role.ui.wear.model.DefaultAppConfirmDialogViewModelFactory
+import com.android.role.controller.model.Role
+import com.android.role.controller.model.Roles
+
+/**
+ * Wear specific version of
+ * [com.android.permissioncontroller.role.ui.handheld.HandheldDefaultAppFragment]
+ */
+class WearDefaultAppFragment : Fragment() {
+ private lateinit var role: Role
+ private lateinit var viewModel: DefaultAppViewModel
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val roleName = arguments?.getString(Intent.EXTRA_ROLE_NAME) ?: ""
+ val user =
+ arguments?.let {
+ BundleCompat.getParcelable(it, Intent.EXTRA_USER, UserHandle::class.java)!!
+ }
+ ?: UserHandle.SYSTEM
+
+ val activity = requireActivity()
+ role =
+ Roles.get(activity)[roleName]
+ ?: let {
+ Log.e(TAG, "Unknown role: $roleName")
+ activity.finish()
+ return null
+ }
+
+ viewModel =
+ ViewModelProvider(this, DefaultAppViewModel.Factory(role, user, activity.application))
+ .get(DefaultAppViewModel::class.java)
+ viewModel.manageRoleHolderStateLiveData.observe(this, this::onManageRoleHolderStateChanged)
+
+ val confirmDialogViewModel =
+ ViewModelProvider(this, DefaultAppConfirmDialogViewModelFactory())
+ .get(DefaultAppConfirmDialogViewModel::class.java)
+
+ return ComposeView(activity).apply {
+ setContent {
+ WearPermissionTheme {
+ WearDefaultAppScreen(
+ WearDefaultAppHelper(
+ activity,
+ user,
+ role,
+ viewModel,
+ confirmDialogViewModel
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun onManageRoleHolderStateChanged(state: Int) {
+ val liveData = viewModel.manageRoleHolderStateLiveData
+ when (state) {
+ ManageRoleHolderStateLiveData.STATE_SUCCESS -> {
+ val packageName = liveData.lastPackageName
+ if (packageName != null) {
+ role.onHolderSelectedAsUser(packageName, liveData.lastUser, requireContext())
+ }
+ liveData.resetState()
+ }
+ ManageRoleHolderStateLiveData.STATE_FAILURE -> liveData.resetState()
+ }
+ }
+
+ companion object {
+ const val TAG = "WearDefaultAppFragment"
+
+ /** Creates a new instance of [WearDefaultAppFragment]. */
+ fun newInstance(roleName: String, user: UserHandle): WearDefaultAppFragment {
+ return WearDefaultAppFragment().apply {
+ arguments =
+ Bundle().apply {
+ putString(Intent.EXTRA_ROLE_NAME, roleName)
+ putParcelable(Intent.EXTRA_USER, user)
+ }
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
new file mode 100644
index 000000000..06b4e61be
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.wear
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.os.UserHandle
+import android.util.Pair
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.role.ui.DefaultAppViewModel
+import com.android.permissioncontroller.role.ui.wear.model.ConfirmDialogArgs
+import com.android.permissioncontroller.role.ui.wear.model.DefaultAppConfirmDialogViewModel
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils
+import com.android.role.controller.model.Role
+
+/** A helper class to retrieve default apps to [WearDefaultAppScreen]. */
+class WearDefaultAppHelper(
+ val context: Context,
+ val user: UserHandle,
+ val role: Role,
+ val viewModel: DefaultAppViewModel,
+ val confirmDialogViewModel: DefaultAppConfirmDialogViewModel
+) {
+ fun getTitle() = context.getString(role.labelResource)
+
+ fun getNonePreference(
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
+ ): WearRoleApplicationPreference? =
+ if (role.shouldShowNone()) {
+ WearRoleApplicationPreference(
+ context = context,
+ label = context.getString(R.string.default_app_none),
+ checked = !hasHolderApplication(qualifyingApplications),
+ onDefaultCheckChanged = { _ -> viewModel.setNoneDefaultApp() }
+ )
+ .apply { icon = context.getDrawable(R.drawable.ic_remove_circle) }
+ } else {
+ null
+ }
+
+ fun getPreferences(
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
+ ): List<WearRoleApplicationPreference> {
+ return qualifyingApplications
+ .map { pair ->
+ val appInfo = pair.first
+ val selected = pair.second
+ WearRoleApplicationPreference(
+ context = context,
+ label = Utils.getFullAppLabel(appInfo, context),
+ checked = selected,
+ onDefaultCheckChanged = { _ ->
+ run {
+ val packageName = appInfo.packageName
+ val confirmationMessage =
+ RoleUiBehaviorUtils.getConfirmationMessage(
+ role,
+ packageName,
+ context
+ )
+ if (confirmationMessage != null) {
+ showConfirmDialog(packageName, confirmationMessage.toString())
+ } else {
+ setDefaultApp(packageName)
+ }
+ }
+ }
+ )
+ .apply {
+ icon = appInfo.loadIcon(context.packageManager)
+ setRestrictionIntent(
+ role.getApplicationRestrictionIntentAsUser(appInfo, user, context)
+ )
+ RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(
+ role,
+ this,
+ appInfo,
+ user,
+ context
+ )
+ }
+ }
+ .toList()
+ }
+
+ private fun showConfirmDialog(packageName: String, message: String) {
+ confirmDialogViewModel.confirmDialogArgs =
+ ConfirmDialogArgs(
+ message = message,
+ onOkButtonClick = {
+ setDefaultApp(packageName)
+ dismissConfirmDialog()
+ },
+ onCancelButtonClick = { dismissConfirmDialog() }
+ )
+ confirmDialogViewModel.showConfirmDialogLiveData.value = true
+ }
+
+ private fun dismissConfirmDialog() {
+ confirmDialogViewModel.confirmDialogArgs = null
+ confirmDialogViewModel.showConfirmDialogLiveData.value = false
+ }
+ private fun setDefaultApp(packageName: String) {
+ viewModel.setDefaultApp(packageName)
+ }
+
+ fun getDescription() = context.getString(role.descriptionResource)
+
+ private fun hasHolderApplication(
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
+ ): Boolean = qualifyingApplications.map { it.second }.contains(true)
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListFragment.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListFragment.kt
new file mode 100644
index 000000000..5e001483a
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListFragment.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.wear
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.role.ui.DefaultAppListViewModel
+
+class WearDefaultAppListFragment : Fragment() {
+ companion object {
+ /** @return a new instance of [WearDefaultAppListFragment]. */
+ fun newInstance(): WearDefaultAppListFragment {
+ return WearDefaultAppListFragment()
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val context = requireContext()
+ val viewModel = ViewModelProvider(this).get(DefaultAppListViewModel::class.java)
+ val user = viewModel.user
+ return ComposeView(requireContext()).apply {
+ setContent {
+ WearDefaultAppListScreen(
+ WearDefaultAppListHelper(context, user),
+ viewModel.liveData
+ )
+ }
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListHelper.kt
new file mode 100644
index 000000000..dda704aef
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListHelper.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.wear
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.role.ui.DefaultAppActivity
+import com.android.permissioncontroller.role.ui.RoleItem
+import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils
+import com.android.role.controller.model.Roles
+
+/** A helper class to retrieve default apps to [WearDefaultAppListScreen]. */
+class WearDefaultAppListHelper(val context: Context, val user: UserHandle) {
+ fun getPreferences(defaultRole: List<RoleItem>): List<WearRolePreference> {
+ return defaultRole
+ .map { roleItem ->
+ WearRolePreference(
+ context = context,
+ label = context.getString(roleItem.role.shortLabelResource),
+ onDefaultClicked = {
+ val roleName: String = roleItem.role.name
+ val role = Roles.get(context)[roleName]
+ var intent =
+ RoleUiBehaviorUtils.getManageIntentAsUser(role!!, user, context)
+ if (intent == null) {
+ intent = DefaultAppActivity.createIntent(roleName, user, context)
+ }
+ context.startActivity(intent)
+ }
+ )
+ .apply {
+ setRestrictionIntent(
+ roleItem.role.getRestrictionIntentAsUser(user, context)
+ )
+ val holderApplicationInfos = roleItem.holderApplicationInfos
+ if (holderApplicationInfos.isEmpty()) {
+ icon = null
+ summary = context.getString(R.string.default_app_none)
+ } else {
+ val holderApplicationInfo = holderApplicationInfos[0]
+ icon = Utils.getBadgedIcon(context, holderApplicationInfo)
+ summary = Utils.getAppLabel(holderApplicationInfo, context)
+ }
+ RoleUiBehaviorUtils.preparePreferenceAsUser(
+ roleItem.role,
+ roleItem.holderApplicationInfos,
+ this,
+ user,
+ context
+ )
+ }
+ }
+ .toList()
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt
new file mode 100644
index 000000000..afee50389
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt
@@ -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.wear
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.LiveData
+import androidx.wear.compose.material.Text
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.elements.chipDefaultColors
+import com.android.permissioncontroller.permission.ui.wear.elements.chipDisabledColors
+import com.android.permissioncontroller.role.ui.RoleItem
+
+@Composable
+fun WearDefaultAppListScreen(
+ helper: WearDefaultAppListHelper,
+ defaultAppsLiveData: LiveData<List<RoleItem>>,
+) {
+ val defaultAppsState = defaultAppsLiveData.observeAsState(emptyList())
+ val defaultApps: List<RoleItem> by remember { derivedStateOf { defaultAppsState.value } }
+ val preferences = helper.getPreferences(defaultApps)
+ var isLoading by remember { mutableStateOf(true) }
+
+ ScrollableScreen(title = stringResource(R.string.default_apps), isLoading = isLoading) {
+ if (preferences.isEmpty()) {
+ item { Text(stringResource(R.string.no_default_apps)) }
+ return@ScrollableScreen
+ }
+ preferences.forEach { pref ->
+ item {
+ Chip(
+ label = pref.label,
+ icon = pref.icon,
+ colors =
+ if (pref.isEnabled()) {
+ chipDefaultColors()
+ } else {
+ chipDisabledColors()
+ },
+ secondaryLabel = pref.summary?.toString(),
+ onClick = pref.getOnClicked(),
+ modifier = Modifier.fillMaxWidth(),
+ labelMaxLines = Int.MAX_VALUE,
+ secondaryLabelMaxLines = Integer.MAX_VALUE
+ )
+ }
+ }
+ }
+ if (isLoading && defaultApps.isNotEmpty()) {
+ isLoading = false
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt
new file mode 100644
index 000000000..a133aa2c3
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.wear
+
+import android.content.pm.ApplicationInfo
+import android.util.Pair
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import androidx.wear.compose.material.ToggleChipDefaults
+import com.android.permissioncontroller.permission.ui.wear.elements.AlertDialog
+import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
+import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipDisabledColors
+import com.android.permissioncontroller.role.ui.wear.model.ConfirmDialogArgs
+
+@Composable
+fun WearDefaultAppScreen(helper: WearDefaultAppHelper) {
+ val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList())
+ val showConfirmDialog =
+ helper.confirmDialogViewModel.showConfirmDialogLiveData.observeAsState(false)
+ var isLoading by remember { mutableStateOf(true) }
+ Box {
+ WearDefaultAppContent(isLoading, roleLiveData.value, helper)
+ ConfirmDialog(
+ showDialog = showConfirmDialog.value,
+ args = helper.confirmDialogViewModel.confirmDialogArgs
+ )
+ }
+ if (isLoading && roleLiveData.value.isNotEmpty()) {
+ isLoading = false
+ }
+}
+
+@Composable
+private fun WearDefaultAppContent(
+ isLoading: Boolean,
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ helper: WearDefaultAppHelper
+) {
+ ScrollableScreen(title = helper.getTitle(), isLoading = isLoading) {
+ helper.getNonePreference(qualifyingApplications)?.let {
+ item {
+ ToggleChip(
+ label = it.label,
+ icon = it.icon,
+ checked = it.checked,
+ onCheckedChanged = it.onDefaultCheckChanged,
+ toggleControl = ToggleChipToggleControl.Radio,
+ labelMaxLine = Integer.MAX_VALUE
+ )
+ }
+ }
+ for (pref in helper.getPreferences(qualifyingApplications)) {
+ item {
+ ToggleChip(
+ label = pref.label,
+ icon = pref.icon,
+ colors =
+ if (pref.isEnabled()) {
+ ToggleChipDefaults.toggleChipColors()
+ } else {
+ toggleChipDisabledColors()
+ },
+ secondaryLabel = pref.summary?.toString(),
+ checked = pref.checked,
+ onCheckedChanged = pref.getOnCheckChanged(),
+ toggleControl = ToggleChipToggleControl.Radio,
+ labelMaxLine = Integer.MAX_VALUE,
+ secondaryLabelMaxLine = Integer.MAX_VALUE
+ )
+ }
+ }
+
+ item { ListFooter(description = helper.getDescription()) }
+ }
+}
+
+@Composable
+private fun ConfirmDialog(showDialog: Boolean, args: ConfirmDialogArgs?) {
+ args?.let {
+ AlertDialog(
+ showDialog = showDialog,
+ message = it.message,
+ onOKButtonClick = it.onOkButtonClick,
+ onCancelButtonClick = it.onCancelButtonClick,
+ scalingLazyListState = rememberScalingLazyListState()
+ )
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
new file mode 100644
index 000000000..728ea8e99
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
@@ -0,0 +1,395 @@
+/*
+ * 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.wear
+
+import android.app.Activity
+import android.app.role.RoleManager
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Process
+import android.os.UserHandle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.android.permissioncontroller.PermissionControllerStatsLog
+import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor
+import com.android.permissioncontroller.role.model.UserDeniedManager
+import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
+import com.android.permissioncontroller.role.ui.RequestRoleViewModel
+import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModel
+import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModelFactory
+import com.android.permissioncontroller.role.utils.PackageUtils
+import com.android.role.controller.model.Role
+import com.android.role.controller.model.Roles
+import java.util.Objects
+
+/** Wear specific version of [com.android.permissioncontroller.role.ui.RequestRoleFragment] */
+class WearRequestRoleFragment : Fragment() {
+ private lateinit var packageName: String
+ private lateinit var roleName: String
+ private lateinit var role: Role
+ private lateinit var viewModel: RequestRoleViewModel
+ private lateinit var wearViewModel: WearRequestRoleViewModel
+ private lateinit var helper: WearRequestRoleHelper
+
+ private var packageRemovalMonitor: PackageRemovalMonitor? = null
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ roleName = arguments?.getString(Intent.EXTRA_ROLE_NAME) ?: ""
+ packageName = arguments?.getString(Intent.EXTRA_PACKAGE_NAME) ?: ""
+ val context: Context = requireContext()
+
+ role =
+ Roles.get(context)[roleName]
+ ?: let {
+ Log.e(TAG, "Unknown role: $roleName")
+ finish()
+ return null
+ }
+ val currentPackageNames =
+ context.getSystemService(RoleManager::class.java)!!.getRoleHolders(roleName)
+ if (currentPackageNames.contains(packageName)) {
+ Log.i(
+ TAG,
+ "Application is already a role holder, role: $roleName, package: $packageName"
+ )
+ reportRequestResult(
+ PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED_ALREADY_GRANTED,
+ null
+ )
+ clearDeniedSetResultOkAndFinish()
+ return null
+ }
+ val appInfo = PackageUtils.getApplicationInfo(packageName, context)
+ if (appInfo == null) {
+ Log.w(TAG, "Unknown application: $packageName")
+ reportRequestResult(
+ PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED,
+ null
+ )
+ finish()
+ return null
+ }
+
+ viewModel =
+ ViewModelProvider(
+ this,
+ RequestRoleViewModel.Factory(role, requireActivity().application)
+ )
+ .get(RequestRoleViewModel::class.java)
+ viewModel.manageRoleHolderStateLiveData.observe(this, this::onManageRoleHolderStateChanged)
+
+ wearViewModel =
+ ViewModelProvider(this, WearRequestRoleViewModelFactory())
+ .get(WearRequestRoleViewModel::class.java)
+
+ savedInstanceState?.let { wearViewModel.onRestoreInstanceState(it) }
+
+ helper =
+ WearRequestRoleHelper(
+ context,
+ appInfo,
+ role,
+ roleName,
+ packageName,
+ viewModel,
+ wearViewModel
+ )
+
+ val onSetAsDefault: (Boolean, String?) -> Unit = { dontAskAgain, selectedPackageName ->
+ run {
+ if (dontAskAgain) {
+ Log.i(
+ TAG,
+ "Request denied with don't ask again, role: $roleName" +
+ ", package: $packageName"
+ )
+ reportRequestResult(
+ PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_ALWAYS,
+ null
+ )
+ setDeniedAlwaysAndFinish()
+ } else {
+ setRoleHolder(selectedPackageName)
+ }
+ }
+ }
+ val onCanceled: () -> Unit = {
+ Log.i(TAG, "Dialog cancelled, role: $roleName , package: $packageName")
+ reportRequestResult(
+ PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED,
+ null
+ )
+ setDeniedOnceAndFinish()
+ }
+
+ return ComposeView(context).apply {
+ setContent { WearRequestRoleScreen(helper, onSetAsDefault, onCanceled) }
+ }
+ }
+
+ private fun onManageRoleHolderStateChanged(state: Int) {
+ val liveData = viewModel.manageRoleHolderStateLiveData
+ when (state) {
+ ManageRoleHolderStateLiveData.STATE_SUCCESS -> {
+ val lastPackageName = liveData.lastPackageName
+ if (lastPackageName != null) {
+ role.onHolderSelectedAsUser(
+ lastPackageName,
+ liveData.lastUser,
+ requireContext()
+ )
+ }
+ if (lastPackageName == packageName) {
+ Log.i(
+ TAG,
+ "Application added as a role holder, role: $roleName, package: " +
+ packageName
+ )
+ clearDeniedSetResultOkAndFinish()
+ } else {
+ Log.i(
+ TAG,
+ "Request denied with another application added as a role holder, " +
+ "role: $roleName, package: $packageName"
+ )
+ setDeniedOnceAndFinish()
+ }
+ }
+ ManageRoleHolderStateLiveData.STATE_FAILURE -> finish()
+ }
+ }
+
+ private fun clearDeniedSetResultOkAndFinish() {
+ UserDeniedManager.getInstance(requireContext()).clearDenied(roleName, packageName)
+ requireActivity().setResult(Activity.RESULT_OK)
+ finish()
+ }
+
+ private fun setDeniedOnceAndFinish() {
+ UserDeniedManager.getInstance(requireContext()).setDeniedOnce(roleName, packageName)
+ finish()
+ }
+
+ private fun reportRequestResult(result: Int, grantedAnotherPackageName: String?) {
+ val holderPackageName: String? = getHolderPackageName()
+ reportRequestResult(
+ getApplicationUid(packageName),
+ packageName,
+ roleName,
+ getQualifyingApplicationCount(),
+ getQualifyingApplicationUid(holderPackageName),
+ holderPackageName,
+ getQualifyingApplicationUid(grantedAnotherPackageName),
+ grantedAnotherPackageName,
+ result
+ )
+ }
+
+ private fun getApplicationUid(packageName: String): Int {
+ val uid: Int = getQualifyingApplicationUid(packageName)
+ if (uid != -1) {
+ return uid
+ }
+ val applicationInfo =
+ PackageUtils.getApplicationInfo(packageName, requireActivity()) ?: return -1
+ return applicationInfo.uid
+ }
+
+ private fun getQualifyingApplicationUid(packageName: String?): Int {
+ if (packageName == null) {
+ return -1
+ }
+ viewModel.roleLiveData.value?.let { qualifyingApplications ->
+ for (qualifyingApplication in qualifyingApplications) {
+ val qualifyingApplicationInfo = qualifyingApplication.first
+ if (Objects.equals(qualifyingApplicationInfo.packageName, packageName)) {
+ return qualifyingApplicationInfo.uid
+ }
+ }
+ }
+ return -1
+ }
+
+ private fun getHolderPackageName(): String? {
+ viewModel.roleLiveData.value?.let { qualifyingApplications ->
+ for (qualifyingApplication in qualifyingApplications) {
+ val isHolderApplication = qualifyingApplication.second
+ if (isHolderApplication) {
+ return qualifyingApplication.first.packageName
+ }
+ }
+ }
+ return null
+ }
+
+ private fun getQualifyingApplicationCount(): Int {
+ return viewModel.roleLiveData.value?.size ?: -1
+ }
+
+ private fun setDeniedAlwaysAndFinish() {
+ UserDeniedManager.getInstance(requireContext()).setDeniedAlways(roleName, packageName)
+ finish()
+ }
+
+ private fun finish() {
+ requireActivity().finish()
+ }
+
+ private fun setRoleHolder(selectedPackageName: String?) {
+ val context: Context = requireContext()
+ val user: UserHandle = Process.myUserHandle()
+ if (selectedPackageName == null) {
+ reportRequestResult(
+ PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_GRANTED_ANOTHER,
+ null
+ )
+ role.onNoneHolderSelectedAsUser(user, context)
+ viewModel.manageRoleHolderStateLiveData.clearRoleHoldersAsUser(
+ roleName,
+ 0,
+ user,
+ context
+ )
+ } else {
+ val isRequestingApplication = selectedPackageName == packageName
+ if (isRequestingApplication) {
+ reportRequestResult(
+ PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED,
+ null
+ )
+ } else {
+ reportRequestResult(
+ PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_GRANTED_ANOTHER,
+ selectedPackageName
+ )
+ }
+ val flags =
+ if (isRequestingApplication) RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP else 0
+ viewModel.manageRoleHolderStateLiveData.setRoleHolderAsUser(
+ roleName,
+ selectedPackageName,
+ true,
+ flags,
+ user,
+ context
+ )
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+ wearViewModel.onSaveInstanceState(outState)
+ }
+
+ override fun onStart() {
+ super.onStart()
+ packageRemovalMonitor =
+ object : PackageRemovalMonitor(requireContext(), packageName) {
+ override fun onPackageRemoved() {
+ Log.w(
+ TAG,
+ "Application is uninstalled, role: $roleName" +
+ ", package: " +
+ packageName
+ )
+ reportRequestResult(
+ PermissionControllerStatsLog
+ .ROLE_REQUEST_RESULT_REPORTED__RESULT__IGNORED,
+ null
+ )
+ finish()
+ }
+ }
+ .apply { register() }
+ }
+
+ override fun onStop() {
+ super.onStop()
+ packageRemovalMonitor?.let {
+ it.unregister()
+ packageRemovalMonitor = null
+ }
+ }
+
+ companion object {
+ const val TAG = "WearRequestRoleFragment"
+
+ /** Creates a new instance of [WearRequestRoleFragment]. */
+ @JvmStatic
+ fun newInstance(roleName: String, packageName: String): WearRequestRoleFragment {
+ return WearRequestRoleFragment().apply {
+ arguments =
+ Bundle().apply {
+ putString(Intent.EXTRA_ROLE_NAME, roleName)
+ putString(Intent.EXTRA_PACKAGE_NAME, packageName)
+ }
+ }
+ }
+
+ @JvmStatic
+ fun reportRequestResult(
+ requestingUid: Int,
+ requestingPackageName: String,
+ roleName: String,
+ qualifyingCount: Int,
+ currentUid: Int,
+ currentPackageName: String?,
+ grantedAnotherUid: Int,
+ grantedAnotherPackageName: String?,
+ result: Int
+ ) {
+ Log.i(
+ TAG,
+ "Role request result requestingUid=$requestingUid" +
+ " requestingPackageName=$requestingPackageName" +
+ " roleName=$roleName" +
+ " qualifyingCount=$qualifyingCount" +
+ " currentUid=$currentUid" +
+ " currentPackageName=$currentPackageName" +
+ " grantedAnotherUid=$grantedAnotherUid" +
+ " grantedAnotherPackageName=$grantedAnotherPackageName" +
+ " result=$result"
+ )
+ PermissionControllerStatsLog.write(
+ PermissionControllerStatsLog.ROLE_REQUEST_RESULT_REPORTED,
+ requestingUid,
+ requestingPackageName,
+ roleName,
+ qualifyingCount,
+ currentUid,
+ currentPackageName,
+ grantedAnotherUid,
+ grantedAnotherPackageName,
+ result
+ )
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
new file mode 100644
index 000000000..c7f14b862
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.wear
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.graphics.drawable.Drawable
+import android.util.Pair
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.role.model.UserDeniedManager
+import com.android.permissioncontroller.role.ui.RequestRoleViewModel
+import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModel
+import com.android.role.controller.model.Role
+
+/** A helper class for [WearRequestRoleScreen]. */
+class WearRequestRoleHelper(
+ val context: Context,
+ val applicationInfo: ApplicationInfo,
+ val role: Role,
+ val roleName: String,
+ val packageName: String,
+ val viewModel: RequestRoleViewModel,
+ val wearViewModel: WearRequestRoleViewModel
+) {
+ fun getIcon() = Utils.getBadgedIcon(context, applicationInfo)
+
+ fun getTitle() =
+ context.getString(role.requestTitleResource, Utils.getAppLabel(applicationInfo, context))
+
+ // Only show this button when the user denied once
+ fun showDontAskButton() =
+ UserDeniedManager.getInstance(context).isDeniedOnce(roleName, packageName)
+
+ fun getNonePreference(
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ selectedPackage: String?
+ ): RequestRolePreference? =
+ if (role.shouldShowNone()) {
+ val hasHolderApplication = hasHolderApplication(qualifyingApplications)
+ RequestRolePreference(
+ packageName = null,
+ label = context.getString(R.string.default_app_none),
+ subTitle =
+ if (!hasHolderApplication) {
+ context.getString(R.string.request_role_current_default)
+ } else {
+ null
+ },
+ icon = context.getDrawable(R.drawable.ic_remove_circle),
+ checked = selectedPackage.isNullOrEmpty(),
+ enabled =
+ if (!wearViewModel.dontAskAgain()) {
+ true
+ } else {
+ !hasHolderApplication
+ },
+ isHolder = !hasHolderApplication
+ )
+ } else {
+ null
+ }
+
+ fun getPreferences(
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ selectedPackage: String?
+ ): List<RequestRolePreference> {
+ return qualifyingApplications
+ .map { qualifyingApplication ->
+ RequestRolePreference(
+ packageName = qualifyingApplication.first.packageName,
+ label = Utils.getAppLabel(qualifyingApplication.first, context),
+ subTitle =
+ if (qualifyingApplication.second) {
+ context.getString(R.string.request_role_current_default)
+ } else {
+ context.getString(role.requestDescriptionResource)
+ },
+ icon = Utils.getBadgedIcon(context, qualifyingApplication.first),
+ checked = qualifyingApplication.first.packageName.equals(selectedPackage),
+ enabled =
+ if (!wearViewModel.dontAskAgain()) {
+ true
+ } else {
+ qualifyingApplication.second
+ },
+ isHolder = qualifyingApplication.second
+ )
+ }
+ .toList()
+ }
+
+ private fun hasHolderApplication(
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
+ ): Boolean = qualifyingApplications.map { it.second }.contains(true)
+
+ fun shouldSetAsDefaultEnabled(enabled: Boolean): Boolean {
+ return enabled && (wearViewModel.dontAskAgain() || !wearViewModel.isHolderChecked)
+ }
+
+ fun initializeHolderPackageName(qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>) {
+ wearViewModel.holderPackageName =
+ qualifyingApplications.find { it.second }?.first?.packageName
+ }
+
+ fun initializeSelectedPackageName() {
+ if (wearViewModel.holderPackageName == null) {
+ wearViewModel.selectedPackageName.value = null
+ } else {
+ wearViewModel.selectedPackageName.value = packageName
+ }
+ }
+
+ data class RequestRolePreference(
+ val label: String,
+ val subTitle: String?,
+ val icon: Drawable?,
+ val checked: Boolean,
+ val enabled: Boolean,
+ val packageName: String?,
+ val isHolder: Boolean
+ )
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
new file mode 100644
index 000000000..13a9cb6d6
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
@@ -0,0 +1,184 @@
+/*
+ * 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.wear
+
+import android.content.pm.ApplicationInfo
+import android.util.Pair
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.MaterialTheme
+import com.android.permissioncontroller.R
+import com.android.permissioncontroller.permission.ui.wear.elements.Chip
+import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter
+import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip
+import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl
+import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipBackgroundColors
+import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
+
+@Composable
+fun WearRequestRoleScreen(
+ helper: WearRequestRoleHelper,
+ onSetAsDefault: (Boolean, String?) -> Unit,
+ onCanceled: () -> Unit
+) {
+ val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList())
+ val manageRoleHolderState =
+ helper.viewModel.manageRoleHolderStateLiveData.observeAsState(
+ ManageRoleHolderStateLiveData.STATE_WORKING
+ )
+ val dontAskAgain = helper.wearViewModel.dontAskAgain.observeAsState(false)
+ val selectedPackageName = helper.wearViewModel.selectedPackageName.observeAsState(null)
+ var isLoading by remember { mutableStateOf(true) }
+
+ if (isLoading && roleLiveData.value.isNotEmpty()) {
+ helper.initializeHolderPackageName(roleLiveData.value)
+ helper.initializeSelectedPackageName()
+ }
+
+ val onCheckedChanged: (Boolean, String?, Boolean) -> Unit = { checked, packageName, isHolder ->
+ if (checked) {
+ helper.wearViewModel.selectedPackageName.value = packageName
+ helper.wearViewModel.isHolderChecked = isHolder
+ }
+ }
+
+ val onDontAskAgainCheckedChanged: (Boolean) -> Unit = { checked ->
+ helper.wearViewModel.dontAskAgain.value = checked
+ if (checked) {
+ helper.initializeSelectedPackageName()
+ }
+ }
+
+ WearRequestRoleContent(
+ isLoading,
+ helper,
+ roleLiveData.value,
+ manageRoleHolderState.value == ManageRoleHolderStateLiveData.STATE_IDLE,
+ dontAskAgain.value,
+ selectedPackageName.value,
+ onCheckedChanged,
+ onDontAskAgainCheckedChanged,
+ onSetAsDefault,
+ onCanceled
+ )
+
+ if (isLoading && roleLiveData.value.isNotEmpty()) {
+ isLoading = false
+ }
+}
+
+@Composable
+internal fun WearRequestRoleContent(
+ isLoading: Boolean,
+ helper: WearRequestRoleHelper,
+ qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ enabled: Boolean,
+ dontAskAgain: Boolean,
+ selectedPackageName: String?,
+ onCheckedChanged: (Boolean, String?, Boolean) -> Unit,
+ onDontAskAgainCheckedChanged: (Boolean) -> Unit,
+ onSetAsDefault: (Boolean, String?) -> Unit,
+ onCanceled: () -> Unit
+) {
+ ScrollableScreen(
+ image = helper.getIcon(),
+ title = helper.getTitle(),
+ showTimeText = false,
+ isLoading = isLoading
+ ) {
+ helper.getNonePreference(qualifyingApplications, selectedPackageName)?.let {
+ item {
+ ToggleChip(
+ label = it.label,
+ icon = it.icon,
+ enabled = enabled && it.enabled,
+ checked = it.checked,
+ onCheckedChanged = { checked ->
+ run { onCheckedChanged(checked, it.packageName, it.isHolder) }
+ },
+ toggleControl = ToggleChipToggleControl.Radio,
+ labelMaxLine = Integer.MAX_VALUE
+ )
+ }
+ it.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } }
+ }
+
+ for (pref in helper.getPreferences(qualifyingApplications, selectedPackageName)) {
+ item {
+ ToggleChip(
+ label = pref.label,
+ icon = pref.icon,
+ enabled = enabled && pref.enabled,
+ checked = pref.checked,
+ onCheckedChanged = { checked ->
+ run { onCheckedChanged(checked, pref.packageName, pref.isHolder) }
+ },
+ toggleControl = ToggleChipToggleControl.Radio,
+ )
+ }
+ pref.subTitle?.let { subTitle -> item { ListFooter(description = subTitle) } }
+ }
+
+ if (helper.showDontAskButton()) {
+ item {
+ ToggleChip(
+ checked = dontAskAgain,
+ enabled = enabled,
+ onCheckedChanged = { checked -> run { onDontAskAgainCheckedChanged(checked) } },
+ label = stringResource(R.string.request_role_dont_ask_again),
+ toggleControl = ToggleChipToggleControl.Checkbox,
+ colors = toggleChipBackgroundColors(),
+ modifier =
+ Modifier.testTag("com.android.permissioncontroller:id/dont_ask_again"),
+ )
+ }
+ }
+
+ item { Spacer(modifier = Modifier.height(14.dp)) }
+
+ item {
+ Chip(
+ label = stringResource(R.string.request_role_set_as_default),
+ textColor = MaterialTheme.colors.background,
+ colors = ChipDefaults.primaryChipColors(),
+ enabled = helper.shouldSetAsDefaultEnabled(enabled),
+ onClick = { onSetAsDefault(dontAskAgain, selectedPackageName) },
+ modifier = Modifier.testTag("android:id/button1"),
+ )
+ }
+ item {
+ Chip(
+ label = stringResource(R.string.cancel),
+ enabled = enabled,
+ onClick = { onCanceled() },
+ modifier = Modifier.testTag("android:id/button2"),
+ )
+ }
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt
new file mode 100644
index 000000000..abaa33a56
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRoleApplicationPreference.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.wear
+
+import android.content.Context
+import android.content.Intent
+import androidx.preference.TwoStatePreference
+import com.android.permissioncontroller.role.ui.RoleApplicationPreference
+
+/**
+ * Role application preference for Wear. The preference is only used to hand over the properties to
+ * ToggleChip.
+ */
+class WearRoleApplicationPreference(
+ context: Context,
+ val label: String,
+ val checked: Boolean,
+ val onDefaultCheckChanged: (Boolean) -> Unit = {},
+ private var restrictionIntent: Intent? = null
+) : TwoStatePreference(context), RoleApplicationPreference {
+ fun getOnCheckChanged(): (Boolean) -> Unit =
+ restrictionIntent?.let { { _ -> context.startActivity(it) } } ?: onDefaultCheckChanged
+
+ override fun setRestrictionIntent(restrictionIntent: Intent?) {
+ this.restrictionIntent = restrictionIntent
+ setEnabled(restrictionIntent == null)
+ }
+
+ override fun asTwoStatePreference(): TwoStatePreference = this
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt
new file mode 100644
index 000000000..43acf4293
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRolePreference.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.wear
+
+import android.content.Context
+import android.content.Intent
+import androidx.preference.Preference
+import com.android.permissioncontroller.role.ui.RolePreference
+import com.android.permissioncontroller.role.ui.TwoTargetPreference.OnSecondTargetClickListener
+import com.android.settingslib.widget.TwoTargetPreference
+
+/** Role preference for Wear. The preference is only used to hand over the properties to Chip. */
+class WearRolePreference(
+ context: Context,
+ val label: String,
+ val onDefaultClicked: () -> Unit = {},
+ private var restrictionIntent: Intent? = null
+) : TwoTargetPreference(context), RolePreference {
+
+ override fun setOnSecondTargetClickListener(listener: OnSecondTargetClickListener?) {
+ // no-op
+ }
+
+ override fun setRestrictionIntent(restrictionIntent: Intent?) {
+ this.restrictionIntent = restrictionIntent
+ setEnabled(restrictionIntent == null)
+ }
+
+ override fun asPreference(): Preference = this
+
+ fun getOnClicked(): () -> Unit =
+ restrictionIntent?.let { { context.startActivity(it) } } ?: onDefaultClicked
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/DefaultAppConfirmDialogViewModel.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/DefaultAppConfirmDialogViewModel.kt
new file mode 100644
index 000000000..514fd9618
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/DefaultAppConfirmDialogViewModel.kt
@@ -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.permissioncontroller.role.ui.wear.model
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+/** ViewModel for a default app confirm dialog. */
+class DefaultAppConfirmDialogViewModel : ViewModel() {
+ /** A livedata which stores whether confirmation dialog is visible. */
+ val showConfirmDialogLiveData = MutableLiveData<Boolean>()
+
+ /** Arguments for a confirmation dialog. */
+ var confirmDialogArgs: ConfirmDialogArgs? = null
+
+ init {
+ showConfirmDialogLiveData.value = false
+ }
+}
+
+/** Factory for a DefaultAppConfirmDialogViewModel */
+class DefaultAppConfirmDialogViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return DefaultAppConfirmDialogViewModel() as T
+ }
+}
+
+/** Data class for arguments of a default app confirm dialog. */
+data class ConfirmDialogArgs(
+ val message: String,
+ val onOkButtonClick: () -> Unit,
+ val onCancelButtonClick: () -> Unit
+)
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt
new file mode 100644
index 000000000..692533916
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/model/WearRequestRoleViewModel.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.wear.model
+
+import android.os.Bundle
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+/** ViewModel for WearRequestRoleScreen. */
+class WearRequestRoleViewModel : ViewModel() {
+ val dontAskAgain = MutableLiveData<Boolean>(false)
+ val selectedPackageName = MutableLiveData<String?>(null)
+ var isHolderChecked: Boolean = false
+ var holderPackageName: String? = null
+
+ fun dontAskAgain(): Boolean {
+ return dontAskAgain.value ?: false
+ }
+
+ fun onSaveInstanceState(outState: Bundle) {
+ outState.putBoolean(STATE_DONT_ASK_AGAIN, dontAskAgain())
+ outState.putString(STATE_SELECTED_PACKAGE_NAME, selectedPackageName.value)
+ outState.putBoolean(STATE_IS_HOLDER_CHECKED, isHolderChecked)
+ }
+
+ fun onRestoreInstanceState(savedInstanceState: Bundle) {
+ dontAskAgain.value = savedInstanceState.getBoolean(STATE_DONT_ASK_AGAIN)
+ selectedPackageName.value = savedInstanceState.getString(STATE_SELECTED_PACKAGE_NAME)
+ isHolderChecked = savedInstanceState.getBoolean(STATE_IS_HOLDER_CHECKED)
+ }
+
+ companion object {
+ const val STATE_DONT_ASK_AGAIN = "WearRequestRoleViewModel.state.DONT_ASK_AGAIN"
+ const val STATE_SELECTED_PACKAGE_NAME =
+ "WearRequestRoleViewModel.state.SELECTED_PACKAGE_NAME"
+ const val STATE_IS_HOLDER_CHECKED = "WearRequestRoleViewModel.state.IS_HOLDER_CHECKED"
+ }
+}
+
+/** Factory for a WearRequestRoleViewModel */
+class WearRequestRoleViewModelFactory : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return WearRequestRoleViewModel() as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
index 6081695b5..c11a74259 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/RoleUiBehaviorUtils.java
@@ -19,21 +19,20 @@ package com.android.permissioncontroller.role.utils;
import android.content.Context;
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 com.android.modules.utils.build.SdkLevel;
+import com.android.permissioncontroller.role.ui.RequestRoleItemView;
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;
+import java.util.List;
+
/**
* Utility methods for Role UI behavior
*/
@@ -63,29 +62,18 @@ public final class RoleUiBehaviorUtils {
}
/**
- * @see RoleUiBehavior#isVisibleAsUser
+ * @see RoleUiBehavior#prepareRequestRoleItemViewAsUser
*/
- public static boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user,
- @NonNull Context context) {
+ public static void prepareRequestRoleItemViewAsUser(@NonNull Role role,
+ @NonNull RequestRoleItemView itemView, @NonNull ApplicationInfo applicationInfo,
+ @NonNull UserHandle user, @NonNull Context context) {
RoleUiBehavior uiBehavior = getUiBehavior(role);
if (uiBehavior == null) {
- return role.isVisible();
+ return;
}
- return role.isVisible() && uiBehavior.isVisibleAsUser(role, user, context);
- }
-
- /**
- * Check whether this role should be visible to user, for current user.
- *
- * @param context the `Context` to retrieve system services
- *
- * @return whether this role should be visible to user.
- */
- public static boolean isVisible(@NonNull Role role, @NonNull Context context) {
- return isVisibleAsUser(role, Process.myUserHandle(), context);
+ uiBehavior.prepareRequestRoleItemViewAsUser(role, itemView, applicationInfo, user, context);
}
-
/**
* @see RoleUiBehavior#getManageIntentAsUser
*/
@@ -103,28 +91,13 @@ public final class RoleUiBehaviorUtils {
* @see RoleUiBehavior#preparePreferenceAsUser
*/
public static void preparePreferenceAsUser(@NonNull Role role,
- @NonNull RolePreference preference, @NonNull UserHandle user,
- @NonNull Context context) {
- prepareUserRestrictionAwarePreferenceAsUser(role, preference, user, context);
-
+ @NonNull List<ApplicationInfo> applicationInfos, @NonNull RolePreference preference,
+ @NonNull UserHandle user, @NonNull Context context) {
RoleUiBehavior uiBehavior = getUiBehavior(role);
if (uiBehavior == null) {
return;
}
- uiBehavior.preparePreferenceAsUser(role, preference, user, context);
- }
-
- /**
- * @see RoleUiBehavior#isApplicationVisibleAsUser
- */
- public static boolean isApplicationVisibleAsUser(@NonNull Role role,
- @NonNull ApplicationInfo applicationInfo, @NonNull UserHandle user,
- @NonNull Context context) {
- RoleUiBehavior uiBehavior = getUiBehavior(role);
- if (uiBehavior == null) {
- return true;
- }
- return uiBehavior.isApplicationVisibleAsUser(role, applicationInfo, user, context);
+ uiBehavior.preparePreferenceAsUser(role, preference, applicationInfos, user, context);
}
/**
@@ -134,8 +107,6 @@ public final class RoleUiBehaviorUtils {
@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;
@@ -145,18 +116,6 @@ public final class RoleUiBehaviorUtils {
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/role/utils/UiUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/UiUtils.java
index e13e55c51..4d3320667 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/utils/UiUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/UiUtils.java
@@ -18,16 +18,12 @@ package com.android.permissioncontroller.role.utils;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
-import androidx.annotation.Px;
/**
* Utility methods about UI.
@@ -37,6 +33,26 @@ public class UiUtils {
private UiUtils() {}
/**
+ * Set enabled state on a view and its children recursively.
+ *
+ * @see androidx.preference.Preference#setEnabledStateOnViews
+ *
+ * @param view the view to be set to enabled or not
+ * @param enabled whether the view should be enabled
+ */
+ public static void setViewTreeEnabled(@NonNull View view, boolean enabled) {
+ view.setEnabled(enabled);
+ if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ int childCount = viewGroup.getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View childView = viewGroup.getChildAt(i);
+ setViewTreeEnabled(childView, enabled);
+ }
+ }
+ }
+
+ /**
* Set whether a view is shown.
*
* @param view the view to be set to shown or not
diff --git a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
index e89470ff6..339b2a12a 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/utils/UserUtils.java
@@ -17,8 +17,6 @@
package com.android.permissioncontroller.role.utils;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -26,6 +24,8 @@ import android.os.UserManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.modules.utils.build.SdkLevel;
+
import java.util.List;
import java.util.Objects;
@@ -37,56 +37,47 @@ public class UserUtils {
private UserUtils() {}
/**
- * Check whether a user is a profile.
+ * Get the work profile of current user, if any.
*
- * @param user the user to check
* @param context the {@code Context} to retrieve system services
- * @return whether the user is a profile
- */
- public static boolean isProfile(@NonNull UserHandle user, @NonNull Context context) {
- return isManagedProfile(user, context) || isCloneProfile(user, context);
- }
-
- /**
- * Check whether a user is a managed profile.
*
- * @param user the user to check
- * @param context the {@code Context} to retrieve system services
- * @return whether the user is a managed profile
+ * @return the work profile of current user, or {@code null} if none
*/
- public static boolean isManagedProfile(@NonNull UserHandle user, @NonNull Context context) {
- Context userContext = getUserContext(context, user);
- UserManager userUserManager = userContext.getSystemService(UserManager.class);
- return userUserManager.isManagedProfile(user.getIdentifier());
- }
+ @Nullable
+ public static UserHandle getWorkProfile(@NonNull Context context) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ List<UserHandle> profiles = userManager.getUserProfiles();
+ UserHandle user = Process.myUserHandle();
- /**
- * Check whether a user is a clone profile.
- *
- * @param user the user to check
- * @param context the {@code Context} to retrieve system services
- * @return whether the user is a clone profile
- */
- public static boolean isCloneProfile(@NonNull UserHandle user, @NonNull Context context) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
- return false;
+ int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; i++) {
+ UserHandle profile = profiles.get(i);
+
+ if (Objects.equals(profile, user)) {
+ continue;
+ }
+ if (!userManager.isManagedProfile(profile.getIdentifier())) {
+ continue;
+ }
+ return profile;
}
- Context userContext = getUserContext(context, user);
- UserManager userUserManager = userContext.getSystemService(UserManager.class);
- return userUserManager.isCloneProfile();
+ return null;
}
/**
- * Get the work profile of current user, if any.
+ * Get the private profile of current user, if any.
*
* @param context the {@code Context} to retrieve system services
*
- * @return the work profile of current user, or {@code null} if none
+ * @return the private profile of current user, or {@code null} if none
*/
@Nullable
- public static UserHandle getWorkProfile(@NonNull Context context) {
- UserManager userManager = context.getSystemService(UserManager.class);
- List<UserHandle> profiles = userManager.getUserProfiles();
+ public static UserHandle getPrivateProfile(@NonNull Context context) {
+ if (!SdkLevel.isAtLeastV()) {
+ return null;
+ }
+
+ List<UserHandle> profiles = context.getSystemService(UserManager.class).getUserProfiles();
UserHandle user = Process.myUserHandle();
int profilesSize = profiles.size();
@@ -96,14 +87,22 @@ public class UserUtils {
if (Objects.equals(profile, user)) {
continue;
}
- if (!userManager.isManagedProfile(profile.getIdentifier())) {
- continue;
+ if (isPrivateProfile(profile, context)) {
+ return profile;
}
- return profile;
}
return null;
}
+ private static boolean isPrivateProfile(@NonNull UserHandle userHandle,
+ @NonNull Context context) {
+ if (!SdkLevel.isAtLeastV() || !android.os.Flags.allowPrivateProfile()) {
+ return false;
+ }
+ Context userContext = context.createContextAsUser(userHandle, /* flags= */ 0);
+ return userContext.getSystemService(UserManager.class).isPrivateProfile();
+ }
+
/**
* Create a context for a user.
*
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/SafetyCenterConstants.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/SafetyCenterConstants.java
index cf96967ff..8ca76d006 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/SafetyCenterConstants.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/SafetyCenterConstants.java
@@ -38,6 +38,9 @@ public class SafetyCenterConstants {
/** Suffix used to identify a source in the Safety Center work profile */
public static final String WORK_PROFILE_SUFFIX = "work";
+ /** Suffix used to identify a source in the Safety Center private profile */
+ public static final String PRIVATE_PROFILE_SUFFIX = "private";
+
/** Intent extra representing the preference key of a search result */
public static final String EXTRA_SETTINGS_FRAGMENT_ARGS_KEY = ":settings:fragment_args_key";
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterBackgroundRefreshJobService.java
index e959d20be..d23a23f7c 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;
}
@@ -94,7 +95,7 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService {
}
if (!safetyCenterManager.isSafetyCenterEnabled()) {
- Log.v(
+ Log.i(
TAG,
"Received a "
+ actionString
@@ -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);
@@ -138,7 +135,7 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService {
public boolean onStartJob(JobParameters params) {
// background thread not required, PC APK makes all API calls in main thread
if (!SafetyCenterJobServiceFlags.areBackgroundRefreshesEnabled()) {
- Log.v(TAG, "Background refreshes are not enabled, skipping job.");
+ Log.i(TAG, "Background refreshes are not enabled, skipping job.");
return false; // job is no longer running
}
SafetyCenterManager safetyCenterManager = this.getSystemService(SafetyCenterManager.class);
@@ -147,7 +144,7 @@ public final class SafetyCenterBackgroundRefreshJobService extends JobService {
return false; // job is no longer running
}
if (!safetyCenterManager.isSafetyCenterEnabled()) {
- Log.v(TAG, "Safety center is not enabled, skipping job.");
+ Log.i(TAG, "Safety center is not enabled, skipping job.");
return false; // job is no longer running
}
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..c5e59d3df 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/service/SafetyCenterSearchIndexablesProvider.kt
@@ -22,6 +22,7 @@ import android.content.res.Resources
import android.database.Cursor
import android.database.MatrixCursor
import android.os.Build
+import android.os.UserHandle
import android.os.UserManager
import android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS
import android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS
@@ -46,13 +47,14 @@ import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.service.BaseSearchIndexablesProvider
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PERSONAL_PROFILE_SUFFIX
+import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX
import com.android.permissioncontroller.safetycenter.ui.SafetyCenterUiFlags
import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel.Pref
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 +69,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 +78,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 +91,7 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
cursor.addSafetySourceRow(
context,
safetySource,
- resourcesContext,
+ safetyCenterResourcesApk,
safetyCenterManager,
screenTitle
)
@@ -107,7 +113,6 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
val context = requireContext()
val safetyCenterManager =
context.getSystemService(SafetyCenterManager::class.java) ?: return cursor
- val userManager = context.getSystemService(UserManager::class.java) ?: return cursor
val keysToRemove = mutableSetOf<String>()
if (safetyCenterManager.isSafetyCenterEnabled) {
@@ -120,7 +125,7 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
keysToRemove,
staticEntryGroupsAreRemovable = SdkLevel.isAtLeastU()
)
- keepActiveEntriesFromRemoval(safetyCenterManager, userManager, keysToRemove)
+ keepActiveEntriesFromRemoval(safetyCenterManager, context, keysToRemove)
} else {
collectAllRemovableKeys(
safetyCenterManager,
@@ -139,11 +144,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,26 +163,28 @@ 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
- fun MatrixCursor.addIndexableRow(title: CharSequence, isWorkProfile: Boolean) =
+ fun MatrixCursor.addIndexableRow(title: CharSequence, profileType: ProfileType) =
newRow()
.add(COLUMN_RANK, 0)
.add(COLUMN_TITLE, title)
.add(COLUMN_KEYWORDS, searchTerms?.let { "$title, $it" } ?: title)
- .add(COLUMN_KEY, safetySource.id.addSuffix(isWorkProfile))
+ .add(COLUMN_KEY, safetySource.id.addSuffix(profileType))
.add(COLUMN_INTENT_ACTION, Intent.ACTION_SAFETY_CENTER)
.add(COLUMN_SCREEN_TITLE, screenTitle)
if (safetySource.id == BIOMETRIC_SOURCE_ID) {
- // correct Biometric Unlock title is only available when
- // Biometric SafetySource have sent the data to SafetyCenter
+ // Correct Biometric Unlock title is only available when Biometric SafetySource have
+ // sent the data to SafetyCenter. Only the main user and the work profile send data for
+ // the Biometric Safety Source.
context.getSystemService(UserManager::class.java)?.let { userManager ->
safetyCenterManager.safetyEntries
.associateBy { it.entryId }
@@ -185,36 +193,50 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
val isWorkProfile = userManager.isManagedProfile(it.key.userId)
if (isWorkProfile) {
isWorkEntryAdded = true
+ addIndexableRow(it.value.title, ProfileType.MANAGED)
} else {
+ addIndexableRow(it.value.title, ProfileType.PRIMARY)
isPersonalEntryAdded = true
}
- addIndexableRow(it.value.title, isWorkProfile)
}
}
}
if (!isPersonalEntryAdded) {
- resourcesContext.getNotEmptyStringOrNull(safetySource.titleResId)?.let {
- addIndexableRow(title = it, isWorkProfile = false)
+ safetyCenterResourcesApk.getNotEmptyStringOrNull(safetySource.titleResId)?.let {
+ addIndexableRow(title = it, ProfileType.PRIMARY)
}
}
- if (!isWorkEntryAdded && safetySource.profile == SafetySource.PROFILE_ALL) {
- resourcesContext.getNotEmptyStringOrNull(safetySource.titleForWorkResId)?.let {
- addIndexableRow(title = it, isWorkProfile = true)
+ if (safetySource.profile == SafetySource.PROFILE_ALL) {
+ if (!isWorkEntryAdded) {
+ safetyCenterResourcesApk
+ .getNotEmptyStringOrNull(safetySource.titleForWorkResId)
+ ?.let { addIndexableRow(title = it, ProfileType.MANAGED) }
+ }
+ if (safetySource.id != BIOMETRIC_SOURCE_ID && isPrivateProfileSupported()) {
+ safetyCenterResourcesApk
+ .getNotEmptyStringOrNull(safetySource.titleForPrivateProfileResId)
+ ?.let { addIndexableRow(title = it, ProfileType.PRIVATE) }
}
}
}
- 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 {
null
}
- private fun String.addSuffix(isWorkProfile: Boolean): String =
- "${this}_${if (isWorkProfile) WORK_PROFILE_SUFFIX else PERSONAL_PROFILE_SUFFIX}"
+ private fun String.addSuffix(profileType: ProfileType): String =
+ "${this}_${
+ when (profileType) {
+ ProfileType.MANAGED -> WORK_PROFILE_SUFFIX
+ ProfileType.PRIVATE -> PRIVATE_PROFILE_SUFFIX
+ ProfileType.PRIMARY -> PERSONAL_PROFILE_SUFFIX
+ }
+ }"
private val SafetyCenterManager.safetySourcesGroupsWithEntries: Sequence<SafetySourcesGroup>
get() =
@@ -243,9 +265,12 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
.asSequence()
.filter { it.type != SAFETY_SOURCE_TYPE_ISSUE_ONLY }
.forEach { safetySource ->
- keysToRemove.add(safetySource.id.addSuffix(isWorkProfile = false))
+ keysToRemove.add(safetySource.id.addSuffix(ProfileType.PRIMARY))
if (safetySource.profile == SafetySource.PROFILE_ALL) {
- keysToRemove.add(safetySource.id.addSuffix(isWorkProfile = true))
+ keysToRemove.add(safetySource.id.addSuffix(ProfileType.MANAGED))
+ if (isPrivateProfileSupported()) {
+ keysToRemove.add(safetySource.id.addSuffix(ProfileType.PRIVATE))
+ }
}
}
}
@@ -253,7 +278,7 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
private fun keepActiveEntriesFromRemoval(
safetyCenterManager: SafetyCenterManager,
- userManager: UserManager,
+ context: Context,
keysToRemove: MutableSet<String>
) {
val safetyCenterData = safetyCenterManager.safetyCenterData
@@ -262,9 +287,7 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
if (entryGroup != null && SafetyCenterUiFlags.getShowSubpages()) {
keysToRemove.remove(entryGroup.id)
}
- entryOrGroup.entries.forEach {
- keepEntryFromRemoval(it.entryId, userManager, keysToRemove)
- }
+ entryOrGroup.entries.forEach { keepEntryFromRemoval(it.entryId, context, keysToRemove) }
}
if (!SdkLevel.isAtLeastU()) {
return
@@ -275,18 +298,25 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
.forEach { staticEntry ->
val entryId = SafetyCenterBundles.getStaticEntryId(safetyCenterData, staticEntry)
if (entryId != null) {
- keepEntryFromRemoval(entryId, userManager, keysToRemove)
+ keepEntryFromRemoval(entryId, context, keysToRemove)
}
}
}
private fun keepEntryFromRemoval(
entryId: SafetyCenterEntryId,
- userManager: UserManager,
+ context: Context,
keysToRemove: MutableSet<String>
) {
- val isWorkProfile = userManager.isManagedProfile(entryId.userId)
- keysToRemove.remove(entryId.safetySourceId.addSuffix(isWorkProfile))
+ val userContext = context.createContextAsUser(UserHandle.of(entryId.userId), /* flags= */ 0)
+ val userUserManager = userContext.getSystemService(UserManager::class.java) ?: return
+ if (userUserManager.isManagedProfile) {
+ keysToRemove.remove(entryId.safetySourceId.addSuffix(ProfileType.MANAGED))
+ } else if (isPrivateProfileSupported() && userUserManager.isPrivateProfile) {
+ keysToRemove.remove(entryId.safetySourceId.addSuffix(ProfileType.PRIVATE))
+ } else {
+ keysToRemove.remove(entryId.safetySourceId.addSuffix(ProfileType.PRIMARY))
+ }
}
private val SafetyCenterManager.safetyEntriesOrGroups: Sequence<SafetyCenterEntryOrGroup>
@@ -302,6 +332,12 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
private val SafetyCenterEntry.entryId: SafetyCenterEntryId
get() = SafetyCenterIds.entryIdFromString(id)
+ private fun isPrivateProfileSupported(): Boolean {
+ return SdkLevel.isAtLeastV() &&
+ com.android.permission.flags.Flags.privateProfileSupported() &&
+ android.os.Flags.allowPrivateProfile()
+ }
+
companion object {
private const val BIOMETRIC_SOURCE_ID = "AndroidBiometrics"
@@ -333,4 +369,10 @@ class SafetyCenterSearchIndexablesProvider : BaseSearchIndexablesProvider() {
return safetyCenterDisabled || subpagesDisabled
}
}
+
+ enum class ProfileType {
+ PRIMARY,
+ MANAGED,
+ PRIVATE
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
index 79fc41249..3ab4faa66 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ClickableDisabledSwitchPreference.java
@@ -111,9 +111,9 @@ public class ClickableDisabledSwitchPreference extends SwitchPreference {
});
if (prefState.getAdmin() != null && prefState.getChecked()) {
- setSummary(R.string.enabled_by_admin);
+ setSummary(com.android.settingslib.widget.restricted.R.string.enabled_by_admin);
} else if (prefState.getAdmin() != null) {
- setSummary(R.string.disabled_by_admin);
+ setSummary(com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
} else if (prefType.equals(Pref.MIC)) {
setSummary(R.string.mic_toggle_description);
} else if (prefType.equals(Pref.CAMERA)) {
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..f08614bf4 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/ComparablePreferenceCategory.kt
@@ -25,13 +25,17 @@ 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(
+ defStyleAttr: Int =
+ getAttr(
context,
- R.attr.preferenceCategoryStyle,
- android.R.attr.preferenceCategoryStyle),
+ androidx.preference.R.attr.preferenceCategoryStyle,
+ android.R.attr.preferenceCategoryStyle
+ ),
defStyleRes: Int = 0
) : PreferenceCategory(context, attrs, defStyleAttr, defStyleRes), ComparablePreference {
@@ -47,8 +51,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..ee23d3131 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/PrivacySubpageFragment.kt
@@ -81,7 +81,7 @@ class PrivacySubpageFragment : SafetyCenterFragment() {
}
override fun renderSafetyCenterData(uiData: SafetyCenterUiData?) {
- Log.d(TAG, "renderSafetyCenterEntryGroup called with $uiData")
+ Log.v(TAG, "renderSafetyCenterEntryGroup called with $uiData")
val entryGroup = uiData?.getMatchingGroup(PRIVACY_SOURCES_GROUP_ID)
if (entryGroup == null) {
Log.w(
@@ -114,11 +114,12 @@ class PrivacySubpageFragment : SafetyCenterFragment() {
subpageIssues,
subpageDismissedIssues,
uiData.resolvedIssues,
- requireActivity().getTaskId())
+ requireActivity().getTaskId()
+ )
}
private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) {
- Log.d(TAG, "updateSafetyCenterEntries called with $entryGroup")
+ Log.v(TAG, "updateSafetyCenterEntries called with $entryGroup")
subpageGenericEntryGroup.removeAll()
subpageControlsExtraEntryGroup.removeAll()
@@ -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..c6f2d146f 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
@@ -26,21 +26,32 @@ import static com.android.permissioncontroller.PermissionControllerStatsLog.PRIV
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.EXTRA_SETTINGS_FRAGMENT_ARGS_KEY;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PERSONAL_PROFILE_SUFFIX;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVACY_SOURCES_GROUP_ID;
+import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX;
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 android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.PreferenceFragmentCompat;
import com.android.permissioncontroller.Constants;
import com.android.permissioncontroller.PermissionControllerStatsLog;
@@ -103,11 +114,67 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
- .add(R.id.content_frame, frag)
+ .add(com.android.settingslib.collapsingtoolbar.R.id.content_frame, frag)
.commitNow();
}
+ configureHomeButton();
+
+ frag.getLifecycle()
+ .addObserver(
+ new LifecycleEventObserver() {
+ @Override
+ public void onStateChanged(
+ LifecycleOwner unused, Lifecycle.Event event) {
+ if (event != Lifecycle.Event.ON_START) {
+ return;
+ }
+ View listView = getListView(frag);
+ if (listView == null) {
+ return;
+ }
+ int paddingBottom = listView.getPaddingBottom();
+ ViewCompat.setOnApplyWindowInsetsListener(
+ listView,
+ (v, windowInsets) -> {
+ Insets insets =
+ windowInsets.getInsets(
+ WindowInsetsCompat.Type.systemBars());
+ v.setPadding(
+ v.getPaddingLeft(),
+ v.getPaddingTop(),
+ v.getPaddingRight(),
+ paddingBottom + insets.bottom);
+ return WindowInsetsCompat.CONSUMED;
+ });
+ }
+ });
+ }
+
+ @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(
+ com.android.settingslib.collapsingtoolbar.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 +182,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();
}
@@ -192,7 +262,7 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
long sessionId =
intent.getLongExtra(Constants.EXTRA_SESSION_ID, Constants.INVALID_SESSION_ID);
- Log.v(
+ Log.i(
TAG,
"privacy source notification metric, source "
+ privacySource
@@ -242,6 +312,8 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
splitKey = preferenceKey.split("_" + PERSONAL_PROFILE_SUFFIX);
} else if (preferenceKey.endsWith(WORK_PROFILE_SUFFIX)) {
splitKey = preferenceKey.split("_" + WORK_PROFILE_SUFFIX);
+ } else if (preferenceKey.endsWith(PRIVATE_PROFILE_SUFFIX)) {
+ splitKey = preferenceKey.split("_" + PRIVATE_PROFILE_SUFFIX);
} else {
return "";
}
@@ -264,4 +336,19 @@ public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
}
return "";
}
+
+ @Nullable
+ private View getListView(Fragment fragment) {
+ if (fragment instanceof PreferenceFragmentCompat) {
+ return ((PreferenceFragmentCompat) fragment).getListView();
+ }
+ if (fragment instanceof SafetyCenterScrollWrapperFragment) {
+ Fragment dashboardFragment =
+ fragment.getChildFragmentManager().findFragmentById(R.id.fragment_container);
+ if (dashboardFragment instanceof PreferenceFragmentCompat) {
+ return ((PreferenceFragmentCompat) dashboardFragment).getListView();
+ }
+ }
+ return null;
+ }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index 940cb2f69..efbd57080 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -19,6 +19,7 @@ package com.android.permissioncontroller.safetycenter.ui;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID;
+import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVACY_SOURCES_GROUP_ID;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.QUICK_SETTINGS_SAFETY_CENTER_FRAGMENT;
import static java.util.Collections.emptyList;
@@ -38,23 +39,29 @@ 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.modules.utils.build.SdkLevel;
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;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/** Dashboard fragment for the Safety Center. */
@RequiresApi(TIRAMISU)
@@ -113,6 +120,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 +134,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 +183,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());
@@ -180,8 +201,7 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
if (uiData == null) return;
SafetyCenterData data = uiData.getSafetyCenterData();
- Log.i(TAG, String.format("renderSafetyCenterData called with: %s", data));
-
+ Log.v(TAG, String.format("renderSafetyCenterData called with: %s", data));
Context context = getContext();
if (context == null) {
return;
@@ -225,6 +245,17 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
boolean isFirstElement = i == 0;
boolean isLastElement = i == size - 1;
+ if (SdkLevel.isAtLeastV()
+ && group != null
+ && Objects.equals(group.getId(), PRIVACY_SOURCES_GROUP_ID)) {
+ // Add an extra header before the privacy sources
+ PreferenceCategory category = new ComparablePreferenceCategory(context);
+ SafetyCenterResourcesApk safetyCenterResourcesApk =
+ new SafetyCenterResourcesApk(requireContext());
+ category.setTitle(safetyCenterResourcesApk.getStringByName("privacy_title"));
+ mEntriesGroup.addPreference(category);
+ }
+
if (SafetyCenterUiFlags.getShowSubpages() && group != null) {
mEntriesGroup.addPreference(
new SafetyHomepageEntryPreference(
@@ -282,19 +313,31 @@ public final class SafetyCenterDashboardFragment extends SafetyCenterFragment {
mStaticEntriesGroup.removeAll();
for (SafetyCenterStaticEntryGroup group : data.getStaticEntryGroups()) {
- PreferenceCategory category = new ComparablePreferenceCategory(context);
- category.setTitle(group.getTitle());
- mStaticEntriesGroup.addPreference(category);
-
- for (SafetyCenterStaticEntry entry : group.getStaticEntries()) {
- category.addPreference(
- new StaticSafetyEntryPreference(
- context,
- requireActivity().getTaskId(),
- entry,
- SafetyCenterBundles.getStaticEntryId(data, entry),
- getSafetyCenterViewModel()));
+ if (group.getTitle().toString().isEmpty()) {
+ // Interpret an empty title as signal to not create a titled category
+ addStaticEntriesTo(context, data, mStaticEntriesGroup, group.getStaticEntries());
+ } else {
+ PreferenceCategory category = new ComparablePreferenceCategory(context);
+ category.setTitle(group.getTitle());
+ mStaticEntriesGroup.addPreference(category);
+ addStaticEntriesTo(context, data, category, group.getStaticEntries());
}
}
}
+
+ private void addStaticEntriesTo(
+ Context context,
+ SafetyCenterData data,
+ PreferenceGroup prefGroup,
+ List<SafetyCenterStaticEntry> entries) {
+ for (SafetyCenterStaticEntry entry : entries) {
+ prefGroup.addPreference(
+ new StaticSafetyEntryPreference(
+ context,
+ requireActivity().getTaskId(),
+ entry,
+ SafetyCenterBundles.getStaticEntryId(data, entry),
+ getSafetyCenterViewModel()));
+ }
+ }
}
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..85079eb1c 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterQsFragment.java
@@ -27,6 +27,7 @@ import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
@@ -41,6 +42,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -149,6 +151,12 @@ 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);
+ root.setOnApplyWindowInsetsListener((v, w) -> {
+ final Insets insets = w.getInsets(WindowInsets.Type.systemBars());
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsets.CONSUMED;
+ });
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..fdade2189 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. */
@@ -72,7 +72,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
}
override fun renderSafetyCenterData(uiData: SafetyCenterUiData?) {
- Log.d(TAG, "renderSafetyCenterEntryGroup called with $uiData")
+ Log.v(TAG, "renderSafetyCenterEntryGroup called with $uiData")
val entryGroup = uiData?.getMatchingGroup(sourceGroupId)
if (entryGroup == null) {
Log.w(TAG, "$sourceGroupId doesn't match any of the existing SafetySourcesGroup IDs")
@@ -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)
@@ -137,7 +136,7 @@ class SafetyCenterSubpageFragment : SafetyCenterFragment() {
}
private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) {
- Log.d(TAG, "updateSafetyCenterEntries called with $entryGroup")
+ Log.v(TAG, "updateSafetyCenterEntries called with $entryGroup")
subpageEntryGroup.removeAll()
for (entry in entryGroup.entries) {
subpageEntryGroup.addPreference(
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/SafetyHomepageEntryPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt
index 0cfc2ee50..e37950264 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyHomepageEntryPreference.kt
@@ -42,13 +42,13 @@ internal class SafetyHomepageEntryPreference(
init {
setTitle(entryGroup.title)
setSummary(entryGroup.summary)
- setIcon(
- SeverityIconPicker.selectIconResId(
+ SeverityIconPicker.selectIconResIdOrNull(
entryGroup.id,
entryGroup.severityLevel,
entryGroup.severityUnspecifiedIconType
)
- )
+ ?.let { setIcon(it) }
+ ?: setIconSpaceReserved(false)
val intent = Intent(Intent.ACTION_SAFETY_CENTER)
intent.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID, entryGroup.id)
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusAnimationSequencer.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusAnimationSequencer.kt
index c48ad734b..a9cab3f85 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusAnimationSequencer.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyStatusAnimationSequencer.kt
@@ -24,7 +24,7 @@ import android.safetycenter.SafetyCenterStatus.OVERALL_SEVERITY_LEVEL_UNKNOWN
* [onStartScanningAnimationStart], [onStartScanningAnimationEnd], etc.) it changes its internal
* state and may provide a presentation instruction in the form of [Action].
*/
-internal class SafetyStatusAnimationSequencer {
+class SafetyStatusAnimationSequencer {
private var isIconChangeAnimationRunning: Boolean = false
private var isScanAnimationRunning: Boolean = false
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/SafetySubpageEntryPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt
index 313798088..b89abde13 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetySubpageEntryPreference.kt
@@ -18,6 +18,7 @@ package com.android.permissioncontroller.safetycenter.ui
import android.content.Context
import android.os.Build
+import android.os.UserHandle
import android.os.UserManager
import android.safetycenter.SafetyCenterEntry
import android.safetycenter.SafetyCenterEntry.IconAction.ICON_ACTION_TYPE_GEAR
@@ -28,8 +29,10 @@ import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
+import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PERSONAL_PROFILE_SUFFIX
+import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel
import com.android.permissioncontroller.safetycenter.ui.view.SafetyEntryCommonViewsManager.Companion.changeEnabledState
@@ -93,10 +96,21 @@ class SafetySubpageEntryPreference(
private fun setupPreferenceKey() {
val entryId: SafetyCenterEntryId = SafetyCenterIds.entryIdFromString(entry.id)
- val isWorkProfile =
- context.getSystemService(UserManager::class.java).isManagedProfile(entryId.userId)
- val keySuffix = if (isWorkProfile) WORK_PROFILE_SUFFIX else PERSONAL_PROFILE_SUFFIX
- setKey("${entryId.safetySourceId}_$keySuffix")
+ val userContext = context.createContextAsUser(UserHandle.of(entryId.userId), /* flags= */ 0)
+ val userUserManager = userContext.getSystemService(UserManager::class.java) ?: return
+ if (userUserManager.isManagedProfile) {
+ setKey("${entryId.safetySourceId}_$WORK_PROFILE_SUFFIX")
+ } else if (isPrivateProfileSupported() && userUserManager.isPrivateProfile) {
+ setKey("${entryId.safetySourceId}_$PRIVATE_PROFILE_SUFFIX")
+ } else {
+ setKey("${entryId.safetySourceId}_$PERSONAL_PROFILE_SUFFIX")
+ }
+ }
+
+ private fun isPrivateProfileSupported(): Boolean {
+ return SdkLevel.isAtLeastV() &&
+ com.android.permission.flags.Flags.privateProfileSupported() &&
+ android.os.Flags.allowPrivateProfile()
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SeverityIconPicker.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SeverityIconPicker.kt
index 752d7ed4a..be47eac73 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SeverityIconPicker.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SeverityIconPicker.kt
@@ -17,6 +17,7 @@ package com.android.permissioncontroller.safetycenter.ui
import android.safetycenter.SafetyCenterEntry
import android.util.Log
+import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.R
import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVACY_SOURCES_GROUP_ID
@@ -25,6 +26,18 @@ internal object SeverityIconPicker {
private val TAG = SeverityIconPicker::class.java.simpleName
@JvmStatic
+ fun selectIconResIdOrNull(
+ id: String,
+ severityLevel: Int,
+ severityUnspecifiedIconType: Int
+ ): Int? {
+ if (SdkLevel.isAtLeastV() && id == PRIVACY_SOURCES_GROUP_ID) {
+ return null
+ }
+ return selectIconResId(id, severityLevel, severityUnspecifiedIconType)
+ }
+
+ @JvmStatic
fun selectIconResId(id: String, severityLevel: Int, severityUnspecifiedIconType: Int): Int {
if (id == PRIVACY_SOURCES_GROUP_ID) {
return R.drawable.ic_privacy
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SnakeCaseConverter.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SnakeCaseConverter.kt
index 4456bfa4d..45d97f05d 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SnakeCaseConverter.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SnakeCaseConverter.kt
@@ -17,7 +17,7 @@
package com.android.permissioncontroller.safetycenter.ui
/** Class used to convert a [String] to the `snake_case` format */
-internal object SnakeCaseConverter {
+object SnakeCaseConverter {
/** Converts a [String] from `camelCase` to `snake_case` */
fun fromCamelCase(input: String): String {
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SpacerPreference.kt
index bb09783be..030b67be9 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)
@@ -88,7 +91,10 @@ internal class SpacerPreference(context: Context, attrs: AttributeSet) :
return
}
- val contentParent = root.findViewById<ViewGroup>(R.id.content_parent)
+ val contentParent =
+ root.findViewById<ViewGroup>(
+ com.android.settingslib.collapsingtoolbar.R.id.content_parent
+ )
if (contentParent == null) {
return
}
@@ -96,27 +102,32 @@ 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
}
- val collapsingToolbar = root.findViewById<View>(R.id.collapsing_toolbar)
- maxKnownToolbarHeight = max(maxKnownToolbarHeight, collapsingToolbar.height)
+ val collapsingToolbar =
+ root.findViewById<View>(
+ com.android.settingslib.collapsingtoolbar.R.id.collapsing_toolbar
+ )
+ 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/StaticSafetyEntryPreference.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java
index 8864da07b..87d8744a8 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/StaticSafetyEntryPreference.java
@@ -19,9 +19,11 @@ package com.android.permissioncontroller.safetycenter.ui;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PERSONAL_PROFILE_SUFFIX;
+import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVATE_PROFILE_SUFFIX;
import static com.android.permissioncontroller.safetycenter.SafetyCenterConstants.WORK_PROFILE_SUFFIX;
import android.content.Context;
+import android.os.UserHandle;
import android.os.UserManager;
import android.safetycenter.SafetyCenterStaticEntry;
import android.text.TextUtils;
@@ -31,6 +33,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.preference.Preference;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
import com.android.safetycenter.internaldata.SafetyCenterEntryId;
@@ -81,17 +84,24 @@ public class StaticSafetyEntryPreference extends Preference implements Comparabl
}
private void setupPreferenceKey(SafetyCenterEntryId entryId) {
- boolean isWorkProfile =
- getContext()
- .getSystemService(UserManager.class)
- .isManagedProfile(entryId.getUserId());
- if (isWorkProfile) {
+ Context userContext = getContext()
+ .createContextAsUser(UserHandle.of(entryId.getUserId()), /* flags= */ 0);
+ UserManager userUserManager = userContext.getSystemService(UserManager.class);
+ if (userUserManager.isManagedProfile()) {
setKey(String.format("%s_%s", entryId.getSafetySourceId(), WORK_PROFILE_SUFFIX));
+ } else if (isPrivateProfileSupported() && userUserManager.isPrivateProfile()) {
+ setKey(String.format("%s_%s", entryId.getSafetySourceId(), PRIVATE_PROFILE_SUFFIX));
} else {
setKey(String.format("%s_%s", entryId.getSafetySourceId(), PERSONAL_PROFILE_SUFFIX));
}
}
+ private Boolean isPrivateProfileSupported() {
+ return SdkLevel.isAtLeastV()
+ && com.android.permission.flags.Flags.privateProfileSupported()
+ && android.os.Flags.allowPrivateProfile();
+ }
+
@Override
public boolean isSameItem(Preference preference) {
return preference instanceof StaticSafetyEntryPreference
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/PrivacyControlsViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/PrivacyControlsViewModel.kt
index 2f5702321..619da1cbd 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/PrivacyControlsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/PrivacyControlsViewModel.kt
@@ -16,7 +16,9 @@
package com.android.permissioncontroller.safetycenter.ui.model
+import android.annotation.SuppressLint
import android.app.Application
+import android.content.ClipboardManager
import android.content.Intent
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.Sensors
@@ -32,6 +34,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.android.modules.utils.build.SdkLevel
+import com.android.permission.flags.Flags
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
import com.android.settingslib.RestrictedLockUtils
@@ -39,10 +43,15 @@ import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
/** Viewmodel for the privacy controls page. */
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+// Suppress warnings related to the camera/mic privacy and clipboard privacy APIs. The PC has the
+// permissions.
+@SuppressLint("MissingPermission")
class PrivacyControlsViewModel(private val app: Application) : AndroidViewModel(app) {
private val sensorPrivacyManager: SensorPrivacyManager =
app.getSystemService(SensorPrivacyManager::class.java)!!
+ private val clipboardManager: ClipboardManager =
+ app.getSystemService(ClipboardManager::class.java)!!
private val userManager: UserManager = app.getSystemService(UserManager::class.java)!!
private val CONFIG_CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS =
@@ -161,6 +170,10 @@ class PrivacyControlsViewModel(private val app: Application) : AndroidViewModel(
}
private fun isClipboardEnabled(): Boolean {
+ if (SdkLevel.isAtLeastV()) {
+ return clipboardManager.areClipboardAccessNotificationsEnabled()
+ }
+
val clipboardDefaultEnabled =
DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_CLIPBOARD,
@@ -176,6 +189,11 @@ class PrivacyControlsViewModel(private val app: Application) : AndroidViewModel(
}
private fun toggleClipboard() {
+ if (SdkLevel.isAtLeastV()) {
+ clipboardManager.setClipboardAccessNotificationsEnabled(!isClipboardEnabled())
+ return
+ }
+
val newState = if (isClipboardEnabled()) 0 else 1
Settings.Secure.putInt(
app.contentResolver,
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..0396db8eb 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterQsViewModel.kt
@@ -45,6 +45,7 @@ import androidx.lifecycle.ViewModelProvider
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.data.LightAppPermGroupLiveData
import com.android.permissioncontroller.permission.data.SmartUpdateMediatorLiveData
+import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.android.permissioncontroller.permission.utils.LocationUtils
@@ -83,7 +84,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 +114,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 +130,8 @@ class SafetyCenterQsViewModel(
LightAppPermissionGroupUsageKey(
usage.packageName,
usage.permissionGroupName,
- UserHandle.getUserHandleForUid(usage.uid))]
+ UserHandle.getUserHandleForUid(usage.uid)
+ )]
?: return
KotlinUtils.revokeForegroundRuntimePermissions(app, group)
@@ -177,13 +183,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 +230,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
@@ -242,6 +252,15 @@ class SafetyCenterQsViewModel(
}
fun getStartViewPermissionUsageIntent(context: Context, usage: PermissionGroupUsage): Intent? {
+ if (
+ !context
+ .getSystemService(LocationManager::class.java)!!
+ .isProviderPackage(usage.packageName)
+ ) {
+ // We should only limit this intent to location provider
+ return null
+ }
+
var intent: Intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
intent.setPackage(usage.packageName)
intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, usage.permissionGroupName)
@@ -250,10 +269,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..c02eafc77
--- /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> View.lazyView(childViewId: Int): Lazy<T> = lazyView { requireViewById(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..b4b21d26b 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/SafetyLabelChangedBroadcastReceiver.kt
@@ -31,11 +31,13 @@ import androidx.annotation.MainThread
import androidx.annotation.RequiresApi
import com.android.permission.safetylabel.SafetyLabel as AppMetadataSafetyLabel
import com.android.permissioncontroller.permission.data.LightPackageInfoLiveData
+import com.android.permissioncontroller.permission.data.get
import com.android.permissioncontroller.permission.data.v34.LightInstallSourceInfoLiveData
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.Utils
+import com.android.permissioncontroller.permission.utils.v34.SafetyLabelUtils
import com.android.permissioncontroller.safetylabel.AppsSafetyLabelHistory.SafetyLabel as SafetyLabelForPersistence
import java.time.Instant
import kotlinx.coroutines.Dispatchers
@@ -56,8 +58,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 +78,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 +132,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.
@@ -137,8 +143,15 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
} else {
context.createContextAsUser(user, 0)
}
+
+ // Asl in Apk (V+) is not supported by permissions
+ if (!SafetyLabelUtils.isAppMetadataSourceSupported(userContext, packageName)) {
+ return
+ }
+
val appMetadataBundle =
try {
+ @Suppress("MissingPermission")
userContext.packageManager.getAppMetadata(packageName)
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "Package $packageName not found while retrieving app metadata")
@@ -155,7 +168,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)
@@ -189,7 +205,7 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
private suspend fun isSafetyLabelSupported(packageUser: Pair<String, UserHandle>): Boolean {
val lightInstallSourceInfo =
- LightInstallSourceInfoLiveData[packageUser].getInitializedValue()
+ LightInstallSourceInfoLiveData[packageUser].getInitializedValue() ?: return false
return lightInstallSourceInfo.supportsSafetyLabel
}
@@ -197,6 +213,7 @@ class SafetyLabelChangedBroadcastReceiver : BroadcastReceiver() {
intentAction == ACTION_PACKAGE_ADDED ||
intentAction == ACTION_PACKAGE_ADDED_PERMISSIONCONTROLLER_FORWARDED
+ @Suppress("MissingPermission")
private fun forwardBroadcastToParentUser(
context: Context,
userManager: UserManager,
@@ -212,12 +229,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/src/com/android/permissioncontroller/safetylabel/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/safetylabel/TEST_MAPPING
index b6e659353..35885d0c3 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetylabel/TEST_MAPPING
+++ b/PermissionController/src/com/android/permissioncontroller/safetylabel/TEST_MAPPING
@@ -11,17 +11,50 @@
],
"presubmit-large": [
{
- "name": "CtsPermission3TestCases",
+ "name": "CtsPermissionUiTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
],
"mainline-presubmit": [
{
- "name": "CtsPermission3TestCases[com.google.android.permission.apex]"
+ "name": "CtsPermissionUiTestCases[com.google.android.permission.apex]",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsPermissionUiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "PermissionControllerMockingTests",
+ "options": [
+ {
+ "include-filter": "com.android.permissioncontroller.tests.mocking.safetylabel"
+ }
+ ]
+ },
+ {
+ "name": "CtsPermissionUiTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsPermissionUiTestCases[com.google.android.permission.apex]"
}
]
-} \ No newline at end of file
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/user/data/repository/v31/UserRepository.kt b/PermissionController/src/com/android/permissioncontroller/user/data/repository/v31/UserRepository.kt
new file mode 100644
index 000000000..e08549c99
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/user/data/repository/v31/UserRepository.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.user.data.repository.v31
+
+import android.app.Application
+import android.content.pm.UserProperties
+import android.os.Build
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.annotation.RequiresApi
+import kotlin.concurrent.Volatile
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/** This repository encapsulate user and user profiles data exposed by [UserManager]. */
+interface UserRepository {
+ /**
+ * Returns a list of UserHandles for profiles associated with the context user, including the
+ * user itself.
+ *
+ * <p>Note that this includes all profile types (not including Restricted profiles).
+ */
+ suspend fun getUserProfilesIncludingCurrentUser(): List<Int>
+
+ /**
+ * Returns whether a user should be shown in the Settings and sharing surfaces depending on the
+ * quiet mode. This is only applicable to profile users since the quiet mode concept is only
+ * applicable to profile users.
+ */
+ suspend fun shouldShowInQuietMode(userId: Int): Boolean
+
+ /**
+ * Returns whether the given profile is in quiet mode or not. Notes: Quiet mode is only
+ * supported for managed profiles.
+ */
+ suspend fun isQuietModeEnabled(userId: Int): Boolean
+
+ companion object {
+ @Volatile private var instance: UserRepository? = null
+
+ fun getInstance(application: Application): UserRepository =
+ instance
+ ?: synchronized(this) { UserRepositoryImpl(application).also { instance = it } }
+ }
+}
+
+class UserRepositoryImpl(
+ application: Application,
+ private val dispatcher: CoroutineDispatcher = Dispatchers.Default,
+) : UserRepository {
+ private val userManager = application.getSystemService(UserManager::class.java)!!
+
+ override suspend fun getUserProfilesIncludingCurrentUser(): List<Int> =
+ withContext(dispatcher) { userManager.userProfiles.map { it.identifier } }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ override suspend fun shouldShowInQuietMode(userId: Int): Boolean =
+ withContext(dispatcher) {
+ val quiteMode = userManager.getUserProperties(UserHandle.of(userId)).showInQuietMode
+ quiteMode != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN
+ }
+
+ override suspend fun isQuietModeEnabled(userId: Int): Boolean =
+ withContext(dispatcher) { userManager.isQuietModeEnabled(UserHandle.of(userId)) }
+}
diff --git a/PermissionController/tests/inprocess/Android.bp b/PermissionController/tests/inprocess/Android.bp
index 78c767f1d..7227e41ad 100644
--- a/PermissionController/tests/inprocess/Android.bp
+++ b/PermissionController/tests/inprocess/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
@@ -33,10 +34,7 @@ android_test {
target_sdk_version: "30",
min_sdk_version: "30",
- srcs: [
- "src/**/*.kt",
- "src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java",
- ],
+ srcs: ["src/**/*.kt"],
libs: [
"android.test.base",
diff --git a/PermissionController/tests/inprocess/AndroidTest.xml b/PermissionController/tests/inprocess/AndroidTest.xml
index 8971a7270..e09fe556b 100644
--- a/PermissionController/tests/inprocess/AndroidTest.xml
+++ b/PermissionController/tests/inprocess/AndroidTest.xml
@@ -30,14 +30,14 @@
<!-- Create place to store apks -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="mkdir -p /data/local/tmp/permissioncontroller/tests/inprocess" />
- <option name="teardown-command" value="rm -rf /data/local/tmp/permissioncontroller/"/>
+ <option name="run-command" value="mkdir -p /data/local/tmp/pc-inprocess" />
+ <option name="teardown-command" value="rm -fr /data/local/tmp/pc-inprocess"/>
</target_preparer>
<!-- Load additional APKs onto device -->
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="push-file" key="AppThatUsesCameraPermission.apk"
- value="/data/local/tmp/permissioncontroller/tests/inprocess/AppThatUsesCameraPermission.apk" />
+ value="/data/local/tmp/pc-inprocess/AppThatUsesCameraPermission.apk" />
</target_preparer>
<!-- Uninstall test-apps -->
diff --git a/PermissionController/tests/inprocess/AppThatUsesCameraPermission/Android.bp b/PermissionController/tests/inprocess/AppThatUsesCameraPermission/Android.bp
index 14198cb6d..a6bd07a49 100644
--- a/PermissionController/tests/inprocess/AppThatUsesCameraPermission/Android.bp
+++ b/PermissionController/tests/inprocess/AppThatUsesCameraPermission/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
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/compat/LinkMovementMethodCompatTest.java b/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java
deleted file mode 100644
index b4b18dbbe..000000000
--- a/PermissionController/tests/inprocess/src/com/android/permissioncontroller/permission/compat/LinkMovementMethodCompatTest.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permissioncontroller.permission.compat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.SystemClock;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.Spanned;
-import android.text.method.MovementMethod;
-import android.text.style.ClickableSpan;
-import android.util.TypedValue;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Test for {@link LinkMovementMethodCompat} without using Mockito, which is unavailable for
- * in-process tests.
- *
- * @see android.text.method.cts.LinkMovementMethodTest
- */
-public class LinkMovementMethodCompatTest {
- private static final String CONTENT = "clickable\nunclickable\nclickable";
-
- private Activity mActivity;
- private LinkMovementMethodCompat mMethod;
- private TextView mView;
- private Spannable mSpannable;
- private MockClickableSpan mClickable0;
- private MockClickableSpan mClickable1;
-
- @Rule
- public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
-
- @Before
- public void setup() throws Throwable {
- mActivity = mActivityRule.getActivity();
- mMethod = new LinkMovementMethodCompat();
-
- // Set the content view with a text view which contains 3 lines,
- mActivityRule.runOnUiThread(() -> mView = new TextViewNoIme(mActivity));
- mView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
- mView.setText(CONTENT, TextView.BufferType.SPANNABLE);
-
- mActivityRule.runOnUiThread(() -> mActivity.setContentView(mView));
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
- mSpannable = (Spannable) mView.getText();
- // make first line clickable
- mClickable0 = markClickable(0, CONTENT.indexOf('\n'));
- // make last line clickable
- mClickable1 = markClickable(CONTENT.lastIndexOf('\n'), CONTENT.length());
- }
-
- @Test
- public void testConstructor() {
- new LinkMovementMethodCompat();
- }
-
- @Test
- public void testGetInstance() {
- MovementMethod method0 = LinkMovementMethodCompat.getInstance();
- assertTrue(method0 instanceof LinkMovementMethodCompat);
-
- MovementMethod method1 = LinkMovementMethodCompat.getInstance();
- assertNotNull(method1);
- assertSame(method0, method1);
- }
-
- @UiThreadTest
- @Test
- public void testOnTouchEvent() {
- assertSelection(mSpannable, -1);
-
- // press on first line (Clickable)
- assertTrue(pressOnLine(0));
- assertSelectClickableLeftToRight(mSpannable, mClickable0);
-
- // release on first line
- mClickable0.clearClickCount();
- assertTrue(releaseOnLine(0));
- mClickable0.assertClickCount(1);
-
- // press on second line (unclickable)
- assertSelectClickableLeftToRight(mSpannable, mClickable0);
- // just clear selection
- pressOnLine(1);
- assertSelection(mSpannable, -1);
-
- // press on last line (Clickable)
- assertTrue(pressOnLine(2));
- assertSelectClickableLeftToRight(mSpannable, mClickable1);
-
- // release on last line
- mClickable1.clearClickCount();
- assertTrue(releaseOnLine(2));
- mClickable1.assertClickCount(1);
-
- // release on second line (unclickable)
- assertSelectClickableLeftToRight(mSpannable, mClickable1);
- // just clear selection
- releaseOnLine(1);
- assertSelection(mSpannable, -1);
- }
-
- @UiThreadTest
- @Test
- public void testOnTouchEvent_outsideLineBounds() {
- assertSelection(mSpannable, -1);
-
- // press on first line (clickable)
- assertTrue(pressOnLine(0));
- assertSelectClickableLeftToRight(mSpannable, mClickable0);
-
- // release above first line
- mClickable0.clearClickCount();
- float x = (mView.getLayout().getLineLeft(0) + mView.getLayout().getLineRight(0)) / 2f;
- float y = -1f;
- assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP));
- mClickable0.assertClickCount(0);
-
- // press on first line (clickable)
- assertTrue(pressOnLine(0));
- assertSelectClickableLeftToRight(mSpannable, mClickable0);
-
- // release to left of first line
- mClickable0.clearClickCount();
- x = mView.getLayout().getLineLeft(0) - 1f;
- y = (mView.getLayout().getLineTop(0) + mView.getLayout().getLineBottom(0)) / 2f;
- assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP));
- mClickable0.assertClickCount(0);
-
- // press on first line (clickable)
- assertTrue(pressOnLine(0));
- assertSelectClickableLeftToRight(mSpannable, mClickable0);
-
- // release to right of first line
- mClickable0.clearClickCount();
- x = mView.getLayout().getLineRight(0) + 1f;
- y = (mView.getLayout().getLineTop(0) + mView.getLayout().getLineBottom(0)) / 2f;
- assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP));
- mClickable0.assertClickCount(0);
-
- // press on last line (clickable)
- assertTrue(pressOnLine(2));
- assertSelectClickableLeftToRight(mSpannable, mClickable1);
-
- // release below last line
- mClickable1.clearClickCount();
- x = (mView.getLayout().getLineLeft(0) + mView.getLayout().getLineRight(0)) / 2f;
- y = mView.getLayout().getHeight() + 1f;
- assertFalse(performMotionAtPoint(x, y, MotionEvent.ACTION_UP));
- mClickable1.assertClickCount(0);
- }
-
- private MockClickableSpan markClickable(final int start, final int end) throws Throwable {
- final MockClickableSpan clickableSpan = new MockClickableSpan();
- mActivityRule.runOnUiThread(() -> mSpannable.setSpan(clickableSpan, start, end,
- Spanned.SPAN_MARK_MARK));
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- return clickableSpan;
- }
- private boolean performMotionAtPoint(float x, float y, int action) {
- long now = SystemClock.uptimeMillis();
- return mMethod.onTouchEvent(mView, mSpannable,
- MotionEvent.obtain(now, now, action, x, y, 0));
- }
-
- private boolean performMotionOnLine(int line, int action) {
- float x = (mView.getLayout().getLineLeft(line) + mView.getLayout().getLineRight(line)) / 2f;
- float y = (mView.getLayout().getLineTop(line) + mView.getLayout().getLineBottom(line)) / 2f;
- return performMotionAtPoint(x, y, action);
- }
-
- private boolean pressOnLine(int line) {
- return performMotionOnLine(line, MotionEvent.ACTION_DOWN);
- }
-
- private boolean releaseOnLine(int line) {
- return performMotionOnLine(line, MotionEvent.ACTION_UP);
- }
-
- private void assertSelection(Spannable spannable, int start, int end) {
- assertEquals(start, Selection.getSelectionStart(spannable));
- assertEquals(end, Selection.getSelectionEnd(spannable));
- }
-
- private void assertSelection(Spannable spannable, int position) {
- assertSelection(spannable, position, position);
- }
-
- private void assertSelectClickableLeftToRight(Spannable spannable,
- ClickableSpan clickableSpan) {
- assertSelection(spannable, spannable.getSpanStart(clickableSpan),
- spannable.getSpanEnd(clickableSpan));
- }
-
- public static class TextViewNoIme extends TextView {
- public TextViewNoIme(@NonNull Context context) {
- super(context);
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- return null;
- }
- }
-
- public static class MockClickableSpan extends ClickableSpan {
- private int mClickCount = 0;
-
- @Override
- public void onClick(@NonNull View widget) {
- ++mClickCount;
- }
-
- public void assertClickCount(int expectedClickCount) {
- assertEquals(expectedClickCount, mClickCount);
- }
-
- public void clearClickCount() {
- mClickCount = 0;
- }
- }
-}
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..bc9e5d6ff 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
@@ -28,8 +28,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
-private const val APK =
- "/data/local/tmp/permissioncontroller/tests/inprocess/AppThatUsesCameraPermission.apk"
+private const val APK = "/data/local/tmp/pc-inprocess/AppThatUsesCameraPermission.apk"
private const val PKG = "com.android.permissioncontroller.tests.appthatrequestpermission"
class AttributionLabelLiveDataTest {
@@ -43,8 +42,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..aa7d7da60 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,51 @@ 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_InvalidOpName() {
+ try {
+ assertThat(PermissionMapping.getPlatformPermissionGroupForOp("invalid_opName"))
+ .isEqualTo(null)
+ } catch (e: IllegalArgumentException) {
+ // ignore wtf may throw in some configuration.
+ }
}
@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..3d425f5f8 100644
--- a/PermissionController/tests/mocking/Android.bp
+++ b/PermissionController/tests/mocking/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
@@ -33,13 +34,10 @@ android_test {
min_sdk_version: "30",
resource_dirs: [
- "main_res",
"test_res",
],
srcs: [
- ":permissioncontroller-sources",
-
"src/**/*.kt",
],
@@ -50,9 +48,14 @@ android_test {
],
static_libs: [
+ "PermissionController-lib",
"iconloader_sc_mainline_prod",
"com.google.android.material_material",
"androidx.transition_transition",
+ "androidx.compose.foundation_foundation",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.runtime_runtime-livedata",
+ "androidx.compose.ui_ui",
"androidx-constraintlayout_constraintlayout",
"androidx.core_core",
"androidx.media_media",
@@ -67,6 +70,7 @@ android_test {
"androidx.leanback_leanback-preference",
"androidx.lifecycle_lifecycle-extensions",
"androidx.lifecycle_lifecycle-common-java8",
+ "androidx.wear.compose_compose-material",
"kotlin-stdlib",
"kotlinx-coroutines-android",
"androidx.navigation_navigation-common-ktx",
@@ -113,8 +117,10 @@ android_test {
"safety-label",
"role-controller",
"lottie",
+ "android.permission.flags-aconfig-java-export",
+ "com.android.permission.flags-aconfig-java-export",
"platform-test-annotations",
-
+ "flag-junit",
"androidx.test.rules",
"androidx.test.ext.truth",
"androidx.test.ext.junit",
@@ -122,23 +128,16 @@ android_test {
"mockito-target-extended-minus-junit4",
],
- proto: {
- type: "lite",
- include_dirs: ["packages/modules/Permission/PermissionController/src/com/android/permissioncontroller"],
- },
-
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
compile_multilib: "both",
- aaptflags: ["--custom-package com.android.permissioncontroller"],
-
test_suites: [
"device-tests",
"mts-permission",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/PermissionController/tests/mocking/main_res b/PermissionController/tests/mocking/main_res
deleted file mode 120000
index 8f3459f94..000000000
--- a/PermissionController/tests/mocking/main_res
+++ /dev/null
@@ -1 +0,0 @@
-../../res \ No newline at end of file
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/AppOpRepositoryTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/AppOpRepositoryTest.kt
new file mode 100644
index 000000000..561c2bc8d
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/AppOpRepositoryTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.appops.data.repository
+
+import android.app.AppOpsManager
+import android.app.AppOpsManager.PackageOps
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel
+import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository
+import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepositoryImpl
+import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
+import com.android.permissioncontroller.tests.mocking.utils.MockUtil.createMockPackageOps
+import com.android.permissioncontroller.tests.mocking.utils.MockUtil.createOpEntry
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class AppOpRepositoryTest {
+ @Mock private lateinit var application: PermissionControllerApplication
+ @Mock private lateinit var context: Context
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var appOpsManager: AppOpsManager
+
+ private lateinit var underTest: AppOpRepository
+ private var mockitoSession: MockitoSession? = null
+
+ private val currentUser = android.os.Process.myUserHandle()
+ private val testPackageName = "test.package"
+ private val testAppId = 100203
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(SdkLevel.isAtLeastS())
+ MockitoAnnotations.initMocks(this)
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+ whenever(PermissionControllerApplication.get()).thenReturn(application)
+ whenever(application.applicationContext).thenReturn(context)
+ whenever(application.packageManager).thenReturn(packageManager)
+ whenever(application.getSystemService(AppOpsManager::class.java)).thenReturn(appOpsManager)
+
+ val permissionRepository = PermissionRepository.getInstance(application)
+ underTest = AppOpRepositoryImpl(application, permissionRepository)
+ }
+
+ @After
+ fun finish() {
+ mockitoSession?.finishMocking()
+ }
+
+ @Test
+ fun verifyPackageAppOpsUsageData() = runTest {
+ val packageOpsData = createPackageOpsMockData()
+ whenever(appOpsManager.getPackagesForOps(any(Array<String>::class.java)))
+ .thenReturn(listOf(packageOpsData))
+
+ val packageOps = underTest.packageAppOpsUsages.take(1).toList().first()
+ val expectedAppOpUsages =
+ packageOpsData.ops.map {
+ AppOpUsageModel(it.opStr, it.getLastAccessTime(OPS_LAST_ACCESS_FLAGS))
+ }
+ assertThat(packageOps.size).isEqualTo(1)
+ assertThat(packageOps[0].packageName).isEqualTo(testPackageName)
+ assertThat(packageOps[0].usages).isEqualTo(expectedAppOpUsages)
+ }
+
+ private fun createPackageOpsMockData(): PackageOps {
+ val opEntries =
+ listOf(
+ createOpEntry(AppOpsManager.OPSTR_FINE_LOCATION, 100),
+ createOpEntry(AppOpsManager.OPSTR_FINE_LOCATION, 200),
+ createOpEntry(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 300),
+ createOpEntry(AppOpsManager.OPSTR_CAMERA, 350),
+ )
+ return createMockPackageOps(testPackageName, opEntries, currentUser.getUid(testAppId))
+ }
+
+ companion object {
+ private const val OPS_LAST_ACCESS_FLAGS =
+ AppOpsManager.OP_FLAG_SELF or
+ AppOpsManager.OP_FLAG_TRUSTED_PROXIED or
+ AppOpsManager.OP_FLAG_TRUSTED_PROXY
+ }
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt
new file mode 100644
index 000000000..1077293d1
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/appops/data/repository/FakeAppOpRepository.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.appops.data.repository
+
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel
+import com.android.permissioncontroller.appops.data.repository.v31.AppOpRepository
+import kotlinx.coroutines.flow.Flow
+
+class FakeAppOpRepository(override val packageAppOpsUsages: Flow<List<PackageAppOpUsageModel>>) :
+ AppOpRepository
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/coroutines/Flows.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/coroutines/Flows.kt
new file mode 100644
index 000000000..cefaf18a7
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/coroutines/Flows.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("OPT_IN_USAGE")
+
+package com.android.permissioncontroller.tests.mocking.coroutines
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+/**
+ * Collect [flow] in a new [Job] and return a getter for the last collected value.
+ *
+ * ```
+ * fun myTest() = runTest {
+ * // ...
+ * val actual by collectLastValue(underTest.flow)
+ * assertThat(actual).isEqualTo(expected)
+ * }
+ * ```
+ */
+fun <T> TestScope.collectLastValue(
+ flow: Flow<T>,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+): FlowValue<T?> {
+ val values by
+ collectValues(
+ flow = flow,
+ context = context,
+ start = start,
+ )
+ return FlowValueImpl { values.lastOrNull() }
+}
+
+/**
+ * Collect [flow] in a new [Job] and return a getter for the collection of values collected.
+ *
+ * ```
+ * fun myTest() = runTest {
+ * // ...
+ * val values by collectValues(underTest.flow)
+ * assertThat(values).isEqualTo(listOf(expected1, expected2, ...))
+ * }
+ * ```
+ */
+fun <T> TestScope.collectValues(
+ flow: Flow<T>,
+ context: CoroutineContext = EmptyCoroutineContext,
+ start: CoroutineStart = CoroutineStart.DEFAULT,
+): FlowValue<List<T>> {
+ val values = mutableListOf<T>()
+ backgroundScope.launch(context, start) { flow.collect(values::add) }
+ return FlowValueImpl {
+ runCurrent()
+ values.toList()
+ }
+}
+
+/** @see collectLastValue */
+interface FlowValue<T> : ReadOnlyProperty<Any?, T> {
+ operator fun invoke(): T
+}
+
+private class FlowValueImpl<T>(private val block: () -> T) : FlowValue<T> {
+ override operator fun invoke(): T = block()
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T = invoke()
+}
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..3de8630bd 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
@@ -31,6 +31,7 @@ import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.hibernation.v31.HibernationController
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
+import com.android.permissioncontroller.permission.utils.ContextCompat
import java.io.File
import org.junit.After
import org.junit.Assert.assertTrue
@@ -49,9 +50,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 +67,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 +78,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 +91,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 +122,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 +137,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 +161,8 @@ class HibernationControllerTest {
0 /* firstInstallTime */,
0 /* lastUpdateTime */,
false /* areAttributionsUserVisible */,
- emptyMap() /* attributionTagsToLabels */)
+ emptyMap() /* attributionTagsToLabels */,
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
}
}
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..99aa4baa9 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
@@ -17,14 +17,18 @@
package com.android.permissioncontroller.tests.mocking.hibernation
import android.app.job.JobScheduler
+import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
+import android.database.ContentObserver
+import android.net.Uri
import android.os.Build
import android.os.SystemClock
import android.os.UserManager
import android.preference.PreferenceManager
import android.provider.DeviceConfig
+import android.provider.Settings
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
@@ -33,40 +37,46 @@ import com.android.permissioncontroller.Constants
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.hibernation.HibernationBroadcastReceiver
import com.android.permissioncontroller.hibernation.ONE_DAY_MS
-import com.android.permissioncontroller.hibernation.PREF_KEY_BOOT_TIME_SNAPSHOT
+import com.android.permissioncontroller.hibernation.PREF_KEY_SYSTEM_TIME_SNAPSHOT
import com.android.permissioncontroller.hibernation.PREF_KEY_ELAPSED_REALTIME_SNAPSHOT
import com.android.permissioncontroller.hibernation.PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING
import com.android.permissioncontroller.hibernation.SNAPSHOT_UNINITIALIZED
import com.android.permissioncontroller.hibernation.getStartTimeOfUnusedAppTracking
import com.google.common.truth.Truth.assertThat
-import java.io.File
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
+import java.io.File
-/**
- * Unit tests for [HibernationPolicy].
- */
+/** Unit tests for [HibernationPolicy]. */
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
class HibernationPolicyTest {
companion object {
private val application = Mockito.mock(PermissionControllerApplication::class.java)
+ private const val USER_SETUP_INCOMPLETE = 0
+ private const val USER_SETUP_COMPLETE = 1
}
@Mock lateinit var jobScheduler: JobScheduler
@Mock lateinit var context: Context
@Mock lateinit var userManager: UserManager
+ @Mock lateinit var contentResolver: ContentResolver
private lateinit var realContext: Context
private lateinit var receiver: HibernationBroadcastReceiver
@@ -77,11 +87,17 @@ 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)
+ .mockStatic(Settings.Secure::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
`when`(PermissionControllerApplication.get()).thenReturn(application)
+ `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt()))
+ .thenReturn(USER_SETUP_COMPLETE)
+ `when`(Settings.Secure.getUriFor(any())).thenReturn(Mockito.mock(Uri::class.java))
realContext = ApplicationProvider.getApplicationContext()
sharedPreferences =
@@ -93,6 +109,7 @@ class HibernationPolicyTest {
filesDir = realContext.cacheDir
`when`(application.filesDir).thenReturn(filesDir)
`when`(context.getSystemService(JobScheduler::class.java)).thenReturn(jobScheduler)
+ `when`(context.contentResolver).thenReturn(contentResolver)
`when`(jobScheduler.schedule(Mockito.any())).thenReturn(JobScheduler.RESULT_SUCCESS)
receiver = HibernationBroadcastReceiver()
@@ -101,20 +118,64 @@ class HibernationPolicyTest {
@After
fun cleanup() {
mockitoSession.finishMocking()
+ sharedPreferences.edit().clear().commit()
val logFile = File(filesDir, Constants.LOGS_TO_DUMP_FILE)
logFile.delete()
}
@Test
+ fun onReceive_userSetupIncomplete_doesNotInitializeStartTime() {
+ `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt()))
+ .thenReturn(USER_SETUP_INCOMPLETE)
+
+ receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED))
+
+ val startTimeOfUnusedAppTracking =
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
+ assertThat(startTimeOfUnusedAppTracking).isEqualTo(SNAPSHOT_UNINITIALIZED)
+ }
+
+ @Test
+ fun onReceive_userSetupCompletes_initializesStartTime() {
+ `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt()))
+ .thenReturn(USER_SETUP_INCOMPLETE)
+
+ receiver.onReceive(context, Intent(Intent.ACTION_BOOT_COMPLETED))
+
+ val contentObserverCaptor = ArgumentCaptor.forClass(ContentObserver::class.java)
+ val uri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
+ verify(contentResolver).registerContentObserver(
+ eq(uri),
+ anyBoolean(),
+ contentObserverCaptor.capture())
+ val contentObserver = contentObserverCaptor.value
+ `when`(Settings.Secure.getInt(any(), eq(Settings.Secure.USER_SETUP_COMPLETE), anyInt()))
+ .thenReturn(USER_SETUP_COMPLETE)
+ contentObserver.onChange(/* selfChange= */ false, uri)
+
+ val startTimeOfUnusedAppTracking =
+ sharedPreferences.getLong(
+ PREF_KEY_START_TIME_OF_UNUSED_APP_TRACKING,
+ SNAPSHOT_UNINITIALIZED
+ )
+ assertThat(startTimeOfUnusedAppTracking).isNotEqualTo(SNAPSHOT_UNINITIALIZED)
+ }
+
+ @Test
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_SYSTEM_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 +195,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_SYSTEM_TIME_SNAPSHOT, SNAPSHOT_UNINITIALIZED)
sharedPreferences
- .edit()
- .putLong(PREF_KEY_BOOT_TIME_SNAPSHOT, systemTimeSnapshot - ONE_DAY_MS)
- .apply()
+ .edit()
+ .putLong(PREF_KEY_SYSTEM_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_SYSTEM_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/repository/FakePermissionRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/repository/FakePermissionRepository.kt
new file mode 100644
index 000000000..28cd2b9e3
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/data/repository/FakePermissionRepository.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.permission.data.repository
+
+import android.content.Context
+import android.os.UserHandle
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+
+class FakePermissionRepository(private val permissionFlags: Map<String, Int> = emptyMap()) :
+ PermissionRepository {
+ override suspend fun getPermissionFlags(
+ permissionName: String,
+ packageName: String,
+ user: UserHandle
+ ): Int {
+ return permissionFlags[permissionName] ?: 0
+ }
+
+ override suspend fun getPermissionGroupLabel(
+ context: Context,
+ groupName: String
+ ): CharSequence {
+ TODO("Not yet implemented")
+ }
+
+ override fun getPermissionGroupsForPrivacyDashboard(): List<String> {
+ return if (SdkLevel.isAtLeastT()) {
+ PermissionMapping.getPlatformPermissionGroups().filter {
+ it != android.Manifest.permission_group.NOTIFICATIONS
+ }
+ } else {
+ PermissionMapping.getPlatformPermissionGroups()
+ }
+ }
+}
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/domain/usecase/GetPermissionUsageUseCaseTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionUsageUseCaseTest.kt
new file mode 100644
index 000000000..d205989d6
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/domain/usecase/GetPermissionUsageUseCaseTest.kt
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.permission.domain.usecase
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel
+import com.android.permissioncontroller.permission.domain.model.v31.PermissionGroupUsageModel
+import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageUseCase
+import com.android.permissioncontroller.permission.utils.Utils
+import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel
+import com.android.permissioncontroller.role.data.repository.v31.RoleRepository
+import com.android.permissioncontroller.tests.mocking.appops.data.repository.FakeAppOpRepository
+import com.android.permissioncontroller.tests.mocking.coroutines.collectLastValue
+import com.android.permissioncontroller.tests.mocking.permission.data.repository.FakePermissionRepository
+import com.android.permissioncontroller.tests.mocking.pm.data.repository.FakePackageRepository
+import com.android.permissioncontroller.tests.mocking.role.data.repository.FakeRoleRepository
+import com.android.permissioncontroller.tests.mocking.user.data.repository.FakeUserRepository
+import com.android.permissioncontroller.user.data.repository.v31.UserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class GetPermissionUsageUseCaseTest {
+ @Mock private lateinit var application: PermissionControllerApplication
+ @Mock private lateinit var context: Context
+
+ private lateinit var mockitoSession: MockitoSession
+ private lateinit var userRepository: UserRepository
+ private lateinit var roleRepository: RoleRepository
+ private lateinit var packageInfos: MutableMap<String, PackageInfoModel>
+ private val currentUser = android.os.Process.myUserHandle()
+ private val testPackageName = "test.package"
+ private val guestUserPkgName = "test.package.guest"
+ private val exemptedPkgName = "test.exempted.package"
+ private val guestUser = UserHandle.of(345)
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .mockStatic(Utils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+
+ whenever(PermissionControllerApplication.get()).thenReturn(application)
+ whenever(application.applicationContext).thenReturn(context)
+
+ userRepository = FakeUserRepository(listOf(currentUser.identifier))
+ roleRepository = FakeRoleRepository(setOf(exemptedPkgName))
+ packageInfos =
+ mapOf(
+ testPackageName to getPackageInfoModel(testPackageName),
+ guestUserPkgName to getPackageInfoModel(guestUserPkgName),
+ exemptedPkgName to getPackageInfoModel(exemptedPkgName),
+ )
+ .toMutableMap()
+ }
+
+ @After
+ fun finish() {
+ mockitoSession.finishMocking()
+ }
+
+ @Test
+ fun guestUserUsageIsFiltered() = runTest {
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ )
+ val guestAppOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 100),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ PackageAppOpUsageModel(guestUserPkgName, guestAppOpsUsage, guestUser.identifier)
+ )
+ )
+ }
+ val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(listOf(PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true)))
+ }
+
+ @Test
+ fun quiteProfileShowUsageInQuietMode() = runTest {
+ Assume.assumeTrue(SdkLevel.isAtLeastV())
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 150),
+ )
+
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ )
+ )
+ }
+ val userRepository =
+ FakeUserRepository(
+ currentUserProfiles = listOf(currentUser.identifier),
+ quietUserProfiles = listOf(currentUser.identifier),
+ showInQuiteModeProfiles = listOf(currentUser.identifier)
+ )
+ val underTest =
+ getPermissionGroupUsageUseCase(appOpsUsageModelFlow, userRepo = userRepository)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(
+ listOf(
+ PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true),
+ PermissionGroupUsageModel(MICROPHONE_PERMISSION_GROUP, 150, true)
+ )
+ )
+ }
+
+ @Test
+ fun quietProfileAppOpsUsageIsFiltered() = runTest {
+ Assume.assumeTrue(SdkLevel.isAtLeastV())
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ )
+ )
+ }
+ val userRepository =
+ FakeUserRepository(
+ currentUserProfiles = listOf(currentUser.identifier),
+ quietUserProfiles = listOf(currentUser.identifier),
+ showInQuiteModeProfiles = emptyList()
+ )
+ val underTest =
+ getPermissionGroupUsageUseCase(appOpsUsageModelFlow, userRepo = userRepository)
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages).isEmpty()
+ }
+
+ @Test
+ fun exemptedPackageIsFiltered() = runTest {
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ )
+ val exemptedOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 100),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ PackageAppOpUsageModel(
+ exemptedPkgName,
+ exemptedOpsUsage,
+ currentUser.identifier
+ )
+ )
+ )
+ }
+ val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(listOf(PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true)))
+ }
+
+ @Test
+ fun permissionNoLongerRequestedOpsAreFiltered() = runTest {
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 100),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ )
+ )
+ }
+ packageInfos[testPackageName] =
+ getPackageInfoModel(
+ testPackageName,
+ requestedPermissions = listOf(RECORD_AUDIO_PERMISSION)
+ )
+ val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(listOf(PermissionGroupUsageModel(MICROPHONE_PERMISSION_GROUP, 100, true)))
+ }
+
+ @Test
+ fun mostRecentAccessedTimestampIsShown() = runTest {
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 150),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 100),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 80),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ )
+ )
+ }
+ val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(
+ listOf(
+ PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 150, true),
+ PermissionGroupUsageModel(MICROPHONE_PERMISSION_GROUP, 100, true)
+ )
+ )
+ }
+
+ @Test
+ fun nonSystemAppsUsageIsUserSensitive() = runTest {
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 100),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ )
+ )
+ }
+ // test package is not a system package
+ val underTest = getPermissionGroupUsageUseCase(appOpsUsageModelFlow)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(
+ listOf(
+ PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true),
+ PermissionGroupUsageModel(MICROPHONE_PERMISSION_GROUP, 100, true)
+ )
+ )
+ }
+
+ @Test
+ fun systemAppsUsageIsUserSensitive() = runTest {
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, 100),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, 100),
+ )
+ val appOpsUsageModelFlow = flow {
+ emit(
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ )
+ )
+ }
+ val permissionFlags =
+ mapOf<String, Int>(
+ CAMERA_PERMISSION to PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED,
+ RECORD_AUDIO_PERMISSION to 0, // not user sensitive when granted
+ )
+ packageInfos[testPackageName] =
+ getPackageInfoModel(
+ testPackageName,
+ requestedPermissions = listOf(CAMERA_PERMISSION, RECORD_AUDIO_PERMISSION),
+ permissionsFlags =
+ listOf(
+ PackageInfo.REQUESTED_PERMISSION_GRANTED,
+ PackageInfo.REQUESTED_PERMISSION_GRANTED
+ ),
+ applicationFlags = ApplicationInfo.FLAG_SYSTEM
+ )
+ val underTest =
+ getPermissionGroupUsageUseCase(appOpsUsageModelFlow, permissionFlags = permissionFlags)
+
+ val permissionGroupUsages by collectLastValue(underTest())
+ assertThat(permissionGroupUsages)
+ .isEqualTo(
+ listOf(
+ PermissionGroupUsageModel(CAMERA_PERMISSION_GROUP, 100, true),
+ PermissionGroupUsageModel(MICROPHONE_PERMISSION_GROUP, 100, false)
+ )
+ )
+ }
+
+ private fun getPackageInfoModel(
+ packageName: String,
+ requestedPermissions: List<String> = listOf(CAMERA_PERMISSION, RECORD_AUDIO_PERMISSION),
+ permissionsFlags: List<Int> = listOf(0, 0),
+ applicationFlags: Int = 0,
+ ) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags)
+
+ private fun getPermissionGroupUsageUseCase(
+ packageAppOpsUsages: Flow<List<PackageAppOpUsageModel>>,
+ permissionFlags: Map<String, Int> = emptyMap(),
+ userRepo: UserRepository = userRepository
+ ): GetPermissionGroupUsageUseCase {
+ val permissionRepository = FakePermissionRepository(permissionFlags)
+ val packageRepository = FakePackageRepository(packageInfos)
+ val appOpUsageRepository = FakeAppOpRepository(packageAppOpsUsages)
+ return GetPermissionGroupUsageUseCase(
+ packageRepository,
+ permissionRepository,
+ appOpUsageRepository,
+ roleRepository,
+ userRepo
+ )
+ }
+
+ companion object {
+ private val CAMERA_PERMISSION = android.Manifest.permission.CAMERA
+ private val RECORD_AUDIO_PERMISSION = android.Manifest.permission.RECORD_AUDIO
+ private val CAMERA_PERMISSION_GROUP = android.Manifest.permission_group.CAMERA
+ private val MICROPHONE_PERMISSION_GROUP = android.Manifest.permission_group.MICROPHONE
+ }
+}
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..bb6468067 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
@@ -19,11 +19,14 @@ package com.android.permissioncontroller.tests.mocking.permission.service
import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.Manifest.permission.ACCESS_MEDIA_LOCATION
+import android.Manifest.permission.BODY_SENSORS
+import android.Manifest.permission.BODY_SENSORS_BACKGROUND
import android.Manifest.permission.READ_CALL_LOG
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
@@ -51,6 +54,7 @@ import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.DeviceUtils
import com.android.permissioncontroller.PermissionControllerApplication
import com.android.permissioncontroller.permission.service.RuntimePermissionsUpgradeController
import com.android.permissioncontroller.tests.mocking.permission.data.dataRepositories
@@ -60,11 +64,13 @@ import org.junit.Assume
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.AdditionalMatchers
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.timeout
@@ -81,10 +87,6 @@ class RuntimePermissionsUpgradeControllerTest {
val application = mock(PermissionControllerApplication::class.java)
init {
- whenever(application.applicationContext).thenReturn(application)
- whenever(application.createContextAsUser(any(), anyInt())).thenReturn(
- application)
-
whenever(application.registerComponentCallbacks(any())).thenAnswer {
val dataRepository = it.arguments[0] as ComponentCallbacks2
@@ -94,34 +96,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 +129,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 +161,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 +184,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,54 +201,67 @@ 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(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.packageManager).thenReturn(packageManager)
+ whenever(application.applicationContext).thenReturn(application)
+ whenever(application.createContextAsUser(any(), anyInt())).thenReturn(application)
+ doReturn(packageManager).`when`(application).packageManager
+ doReturn(permissionManager)
+ .`when`(application)
+ .getSystemService(PermissionManager::class.java)
+ doReturn(activityManager).`when`(application).getSystemService(ActivityManager::class.java)
+ doReturn(appOpsManager).`when`(application).getSystemService(AppOpsManager::class.java)
+ doReturn(locationManager).`when`(application).getSystemService(LocationManager::class.java)
+ doReturn(userManager).`when`(application).getSystemService(UserManager::class.java)
+ doReturn(jobScheduler).`when`(application).getSystemService(JobScheduler::class.java)
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.
whenever(packageManager.queryPermissionsByGroup(any(), anyInt())).thenAnswer {
mutableListOf<PermissionInfo>()
}
+
+ whenever(packageManager.hasSystemFeature(any())).thenAnswer {
+ val featureName = it.arguments[0] as String
+
+ InstrumentationRegistry.getInstrumentation()
+ .getTargetContext()
+ .packageManager
+ .hasSystemFeature(featureName)
+ }
}
- /**
- * 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 +272,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 +313,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 +323,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 +333,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 +344,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 +355,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 +365,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 +375,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 +385,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 +396,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 +412,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 +428,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 +444,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 +464,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 +484,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 +503,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 +530,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 +561,173 @@ 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)
+ }
+
+ @Test
+ fun ensureDatabaseResetToLatestIfAboveLatest() {
+ setInitialDatabaseVersion(Int.MAX_VALUE)
+ upgradeIfNeeded()
+ verify(permissionManager).runtimePermissionsVersion =
+ AdditionalMatchers.not(eq(Int.MAX_VALUE))
+ }
+
+ @Test
+ fun bodySensorsInheritToBodySensorsBackgroundWhenBodySensorsWasGrantedAndTargetingR() {
+ Assume.assumeTrue(DeviceUtils.isWear(application))
+ Assume.assumeTrue(SdkLevel.isAtLeastT())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+ setInitialDatabaseVersion(9)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(BODY_SENSORS, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ Permission(BODY_SENSORS_BACKGROUND, isGranted = false),
+ targetSdkVersion = 30
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyGranted(TEST_PKG_NAME, BODY_SENSORS_BACKGROUND)
+ }
+
+ @Test
+ fun bodySensorsNotInheritToBodySensorsBackgroundWhenBodySensorsWasNotGrantedAndTargetingR() {
+ Assume.assumeTrue(DeviceUtils.isWear(application))
+ Assume.assumeTrue(SdkLevel.isAtLeastT())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+ setInitialDatabaseVersion(9)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(BODY_SENSORS, isGranted = false, flags = FLAG_PERMISSION_USER_SET),
+ Permission(BODY_SENSORS_BACKGROUND, isGranted = false),
+ targetSdkVersion = 30
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyNotGranted(TEST_PKG_NAME, BODY_SENSORS_BACKGROUND)
+ }
+
+ @Test
+ fun bodySensorsInheritToBodySensorsBackgroundWhenBodySensorsWasGrantedAndTargetingT() {
+ Assume.assumeTrue(DeviceUtils.isWear(application))
+ Assume.assumeTrue(SdkLevel.isAtLeastT())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+ setInitialDatabaseVersion(9)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(BODY_SENSORS, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ Permission(BODY_SENSORS_BACKGROUND, isGranted = false),
+ targetSdkVersion = 33
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyGranted(TEST_PKG_NAME, BODY_SENSORS_BACKGROUND)
+ }
+
+ @Test
+ fun bodySensorsNotInheritToBodySensorsBackgroundWhenBodySensorsWasNotGrantedAndTargetingT() {
+ Assume.assumeTrue(DeviceUtils.isWear(application))
+ Assume.assumeTrue(SdkLevel.isAtLeastT())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+ setInitialDatabaseVersion(9)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(BODY_SENSORS, isGranted = false, flags = FLAG_PERMISSION_USER_SET),
+ Permission(BODY_SENSORS_BACKGROUND, isGranted = false),
+ targetSdkVersion = 33
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyNotGranted(TEST_PKG_NAME, BODY_SENSORS_BACKGROUND)
+ }
+
+ @Test
+ fun bodySensorsNotInheritToBodySensorsBackgroundWhenBackgroundNotDeclaredAndTargetingT() {
+ Assume.assumeTrue(DeviceUtils.isWear(application))
+ Assume.assumeTrue(SdkLevel.isAtLeastT())
+ whenever(packageManager.isDeviceUpgrading).thenReturn(true)
+ setInitialDatabaseVersion(9)
+ setPackages(
+ Package(
+ TEST_PKG_NAME,
+ Permission(BODY_SENSORS, isGranted = true, flags = FLAG_PERMISSION_USER_SET),
+ targetSdkVersion = 33
+ )
+ )
+
+ upgradeIfNeeded()
+
+ verifyNotGranted(TEST_PKG_NAME, BODY_SENSORS_BACKGROUND)
+ }
+
@After
fun resetSystem() {
// Send low memory notifications for all data repositories which will clear cached data
@@ -575,12 +756,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/PermissionUsageViewModelTest.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageViewModelTest.kt
new file mode 100644
index 000000000..38390ca64
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/permission/ui/model/PermissionUsageViewModelTest.kt
@@ -0,0 +1,229 @@
+/*
+ * 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.mocking.permission.ui.model
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerApplication
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel
+import com.android.permissioncontroller.appops.data.model.v31.PackageAppOpUsageModel.AppOpUsageModel
+import com.android.permissioncontroller.permission.data.repository.v31.PermissionRepository
+import com.android.permissioncontroller.permission.domain.usecase.v31.GetPermissionGroupUsageUseCase
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel
+import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState
+import com.android.permissioncontroller.permission.utils.PermissionMapping
+import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel
+import com.android.permissioncontroller.tests.mocking.appops.data.repository.FakeAppOpRepository
+import com.android.permissioncontroller.tests.mocking.coroutines.collectLastValue
+import com.android.permissioncontroller.tests.mocking.permission.data.repository.FakePermissionRepository
+import com.android.permissioncontroller.tests.mocking.pm.data.repository.FakePackageRepository
+import com.android.permissioncontroller.tests.mocking.role.data.repository.FakeRoleRepository
+import com.android.permissioncontroller.tests.mocking.user.data.repository.FakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class PermissionUsageViewModelTest {
+ @Mock private lateinit var application: PermissionControllerApplication
+ @Mock private lateinit var context: Context
+ private var mockitoSession: MockitoSession? = null
+
+ private lateinit var permissionRepository: PermissionRepository
+ private val currentUser = android.os.Process.myUserHandle()
+ private val testPackageName = "test.package"
+ private val systemPackageName = "test.package.system"
+ private lateinit var packageInfos: MutableMap<String, PackageInfoModel>
+ @Before
+ fun setup() {
+ Assume.assumeTrue(SdkLevel.isAtLeastS())
+ MockitoAnnotations.initMocks(this)
+ mockitoSession =
+ ExtendedMockito.mockitoSession()
+ .mockStatic(PermissionControllerApplication::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+
+ whenever(PermissionControllerApplication.get()).thenReturn(application)
+ whenever(application.applicationContext).thenReturn(context)
+ PermissionMapping.addHealthPermissionsToPlatform(setOf("health1"))
+
+ val permissionFlags =
+ mapOf<String, Int>(
+ CAMERA_PERMISSION to PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED,
+ RECORD_AUDIO_PERMISSION to 0, // not user sensitive
+ )
+ permissionRepository = FakePermissionRepository(permissionFlags)
+ packageInfos =
+ mapOf(
+ testPackageName to getPackageInfoModel(testPackageName),
+ systemPackageName to
+ getPackageInfoModel(
+ systemPackageName,
+ applicationFlags = ApplicationInfo.FLAG_SYSTEM
+ ),
+ )
+ .toMutableMap()
+ }
+
+ @After
+ fun finish() {
+ mockitoSession?.finishMocking()
+ }
+
+ @Test
+ fun allPermissionGroupsAreShown() = runTest {
+ val permissionUsageViewModel =
+ PermissionUsageViewModel(
+ application,
+ permissionRepository,
+ getPermissionGroupUsageUseCase(),
+ backgroundScope,
+ StandardTestDispatcher(testScheduler),
+ is7DayToggleEnabled = true
+ )
+
+ val uiData =
+ checkNotNull(
+ collectLastValue(permissionUsageViewModel.getPermissionUsagesUiDataFlow()).invoke()
+ )
+ as PermissionUsagesUiState.Success
+
+ val expectedPermissions = PermissionMapping.getPlatformPermissionGroups().toMutableSet()
+ if (SdkLevel.isAtLeastT()) {
+ expectedPermissions.remove(android.Manifest.permission_group.NOTIFICATIONS)
+ }
+ assertThat(uiData.permissionGroupUsageCount.keys).isEqualTo(expectedPermissions)
+ }
+
+ @Test
+ fun permissionGroupsCountNonSystemApps() = runTest {
+ val timestamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5)
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, timestamp),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, timestamp),
+ )
+ val appOpsUsageModels =
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ PackageAppOpUsageModel(systemPackageName, appOpsUsage, currentUser.identifier),
+ )
+ val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels)
+ val permissionUsageViewModel =
+ PermissionUsageViewModel(
+ application,
+ permissionRepository,
+ permissionUsageUseCase,
+ backgroundScope,
+ StandardTestDispatcher(testScheduler),
+ is7DayToggleEnabled = true
+ )
+ val uiData =
+ checkNotNull(
+ collectLastValue(permissionUsageViewModel.getPermissionUsagesUiDataFlow()).invoke()
+ )
+ as PermissionUsagesUiState.Success
+ val permissionGroupsCount = uiData.permissionGroupUsageCount
+ assertThat(permissionGroupsCount[CAMERA_PERMISSION_GROUP]).isEqualTo(2)
+ assertThat(permissionGroupsCount[MICROPHONE_PERMISSION_GROUP]).isEqualTo(1)
+ }
+
+ @Test
+ fun permissionGroupsCountAllApps() = runTest {
+ val timestamp = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5)
+ val appOpsUsage =
+ listOf(
+ AppOpUsageModel(AppOpsManager.OPSTR_CAMERA, timestamp),
+ AppOpUsageModel(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, timestamp),
+ )
+ val appOpsUsageModels =
+ listOf(
+ PackageAppOpUsageModel(testPackageName, appOpsUsage, currentUser.identifier),
+ PackageAppOpUsageModel(systemPackageName, appOpsUsage, currentUser.identifier),
+ )
+ val permissionUsageUseCase = getPermissionGroupUsageUseCase(appOpsUsageModels)
+ val permissionUsageViewModel =
+ PermissionUsageViewModel(
+ application,
+ permissionRepository,
+ permissionUsageUseCase,
+ backgroundScope,
+ StandardTestDispatcher(testScheduler),
+ is7DayToggleEnabled = true
+ )
+
+ collectLastValue(permissionUsageViewModel.getPermissionUsagesUiDataFlow()).invoke()
+ val uiData =
+ permissionUsageViewModel.updateShowSystem(true) as PermissionUsagesUiState.Success
+ val permissionGroupsCount = uiData.permissionGroupUsageCount
+ assertThat(permissionGroupsCount[CAMERA_PERMISSION_GROUP]).isEqualTo(2)
+ assertThat(permissionGroupsCount[MICROPHONE_PERMISSION_GROUP]).isEqualTo(2)
+ }
+
+ private fun getPermissionGroupUsageUseCase(
+ packageAppOpsUsages: List<PackageAppOpUsageModel> = emptyList(),
+ ): GetPermissionGroupUsageUseCase {
+ val userRepository = FakeUserRepository(listOf(currentUser.identifier))
+ val roleRepository = FakeRoleRepository()
+ val packageRepository = FakePackageRepository(packageInfos)
+ val appOpUsageRepository = FakeAppOpRepository(flowOf(packageAppOpsUsages))
+ return GetPermissionGroupUsageUseCase(
+ packageRepository,
+ permissionRepository,
+ appOpUsageRepository,
+ roleRepository,
+ userRepository
+ )
+ }
+
+ private fun getPackageInfoModel(
+ packageName: String,
+ requestedPermissions: List<String> = listOf(CAMERA_PERMISSION, RECORD_AUDIO_PERMISSION),
+ permissionsFlags: List<Int> =
+ listOf(
+ PackageInfo.REQUESTED_PERMISSION_GRANTED,
+ PackageInfo.REQUESTED_PERMISSION_GRANTED
+ ),
+ applicationFlags: Int = 0,
+ ) = PackageInfoModel(packageName, requestedPermissions, permissionsFlags, applicationFlags)
+
+ companion object {
+ private val CAMERA_PERMISSION = android.Manifest.permission.CAMERA
+ private val RECORD_AUDIO_PERMISSION = android.Manifest.permission.RECORD_AUDIO
+ private val CAMERA_PERMISSION_GROUP = android.Manifest.permission_group.CAMERA
+ private val MICROPHONE_PERMISSION_GROUP = android.Manifest.permission_group.MICROPHONE
+ }
+}
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 0f4216066..899a026c4 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 be6518b23..8bd61ebe6 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
@@ -24,6 +24,7 @@ import android.app.AppOpsManager.MODE_FOREGROUND
import android.app.AppOpsManager.MODE_IGNORED
import android.app.AppOpsManager.permissionToOp
import android.app.Application
+import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED
import android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME
@@ -41,11 +42,13 @@ import android.os.Build
import android.os.UserHandle
import android.permission.PermissionManager
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.modules.utils.build.SdkLevel
import com.android.permissioncontroller.permission.model.livedatatypes.LightAppPermGroup
import com.android.permissioncontroller.permission.model.livedatatypes.LightPackageInfo
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermGroupInfo
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermInfo
import com.android.permissioncontroller.permission.model.livedatatypes.LightPermission
+import com.android.permissioncontroller.permission.utils.ContextCompat
import com.android.permissioncontroller.permission.utils.KotlinUtils
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -64,7 +67,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
@@ -101,8 +105,9 @@ class GrantRevokeTests {
}
}
- @Mock
- val app: Application = mock(Application::class.java)
+ @Mock val app: Application = mock(Application::class.java)
+
+ @Mock val context: Context = mock(Context::class.java)
/**
* Create a mock Application object, with a mock packageManager, AppOpsManager, and
@@ -114,16 +119,24 @@ class GrantRevokeTests {
`when`(app.packageManager).thenReturn(mock(PackageManager::class.java))
val aom: AppOpsManager = mock(AppOpsManager::class.java)
+
+ if (SdkLevel.isAtLeastU()) {
+ `when`(context.deviceId).thenReturn(ContextCompat.DEVICE_ID_DEFAULT)
+ }
+ `when`(context.packageManager).thenReturn(mock(PackageManager::class.java))
+ `when`(context.getSystemService(PermissionManager::class.java))
+ .thenReturn(mock(PermissionManager::class.java))
// Return an invalid app op state, so setOpMode will always attempt to change the op state
`when`(aom.unsafeCheckOpRaw(anyString(), anyInt(), nullable(String::class.java)))
.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))
+ `when`(app.applicationContext).thenReturn(context)
}
/**
@@ -142,19 +155,35 @@ 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(),
+ ContextCompat.DEVICE_ID_DEFAULT
+ )
}
/**
@@ -163,13 +192,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,
@@ -179,11 +208,24 @@ 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)
- 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
+ )
+ return LightPermission(
+ pkgInfo,
+ permInfo,
+ pkgInfo.requestedPermissionsFlags[pkgInfo.requestedPermissions.indexOf(permName)] ==
+ PERMISSION_GRANTED,
+ flags,
+ foregroundPerms
+ )
}
/**
@@ -201,12 +243,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> {
@@ -251,28 +292,32 @@ class GrantRevokeTests {
val flags = state.second
assertWithMessage("permission $permName grant state incorrect")
- .that(perms[permName]?.isGrantedIncludingAppOp).isEqualTo(granted)
+ .that(perms[permName]?.isGrantedIncludingAppOp)
+ .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,
@@ -281,7 +326,7 @@ class GrantRevokeTests {
expectedFlags: Int = NO_FLAGS,
originalFlags: Int = NO_FLAGS
) {
- val pm = app.packageManager
+ val pm = context.packageManager
if (expectPermChange) {
if (expectPermGranted) {
verify(pm).grantRuntimePermission(TEST_PACKAGE_NAME, permName, TEST_USER)
@@ -294,11 +339,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)
+ )
}
}
@@ -308,7 +365,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(
@@ -353,8 +410,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)
@@ -379,17 +440,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)
}
@@ -408,8 +480,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)
@@ -434,14 +510,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)
}
@@ -462,26 +542,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)
}
@@ -495,20 +583,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
+ )
}
/**
@@ -528,16 +624,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)
}
@@ -558,25 +662,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)
}
@@ -596,8 +708,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)
@@ -620,8 +736,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)
@@ -643,8 +763,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)
@@ -652,9 +776,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)
@@ -680,8 +802,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()
@@ -695,23 +821,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)
@@ -719,9 +847,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))
@@ -733,8 +859,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)
@@ -742,9 +872,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))
@@ -756,8 +884,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)
@@ -765,9 +897,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))
@@ -780,16 +910,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)
}
@@ -807,8 +945,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)
@@ -834,13 +976,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)
}
@@ -861,24 +1007,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)
}
@@ -899,15 +1053,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)
}
@@ -928,13 +1086,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()
@@ -944,14 +1106,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() {
@@ -965,8 +1127,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)
@@ -975,8 +1142,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() {
@@ -989,8 +1156,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)
@@ -1036,8 +1206,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)
@@ -1062,8 +1236,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)
@@ -1087,8 +1266,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)
@@ -1097,8 +1281,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() {
@@ -1112,8 +1296,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..2af19480d 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.permission.utils.StringUtils
+import com.android.permissioncontroller.tests.mocking.R
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/pm/data/repository/FakePackageRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/FakePackageRepository.kt
new file mode 100644
index 000000000..29557b0b2
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/pm/data/repository/FakePackageRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.pm.data.repository
+
+import android.os.UserHandle
+import com.android.permissioncontroller.pm.data.model.v31.PackageInfoModel
+import com.android.permissioncontroller.pm.data.repository.v31.PackageRepository
+
+class FakePackageRepository(private val packages: Map<String, PackageInfoModel> = emptyMap()) :
+ PackageRepository {
+ override suspend fun getPackageInfo(
+ packageName: String,
+ user: UserHandle,
+ flags: Int
+ ): PackageInfoModel? {
+ return packages[packageName]
+ }
+}
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..cc3b096a8 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/role/data/repository/FakeRoleRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/data/repository/FakeRoleRepository.kt
new file mode 100644
index 000000000..f26020099
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/role/data/repository/FakeRoleRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.role.data.repository
+
+import com.android.permissioncontroller.role.data.repository.v31.RoleRepository
+
+class FakeRoleRepository(private val exemptedPackages: Set<String> = emptySet()) : RoleRepository {
+ override suspend fun getExemptedPackages(): Set<String> = exemptedPackages
+}
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/mocking/src/com/android/permissioncontroller/tests/mocking/user/data/repository/FakeUserRepository.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/user/data/repository/FakeUserRepository.kt
new file mode 100644
index 000000000..e05723d37
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/user/data/repository/FakeUserRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.user.data.repository
+
+import android.os.Process
+import com.android.permissioncontroller.user.data.repository.v31.UserRepository
+
+class FakeUserRepository(
+ private val currentUserProfiles: List<Int> = listOf(Process.myUserHandle().identifier),
+ private val quietUserProfiles: List<Int> = emptyList(),
+ private val showInQuiteModeProfiles: List<Int> = emptyList()
+) : UserRepository {
+ override suspend fun getUserProfilesIncludingCurrentUser(): List<Int> = currentUserProfiles
+
+ override suspend fun shouldShowInQuietMode(userId: Int): Boolean =
+ showInQuiteModeProfiles.contains(userId)
+
+ override suspend fun isQuietModeEnabled(userId: Int): Boolean =
+ quietUserProfiles.contains(userId)
+}
diff --git a/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/utils/MockUtil.kt b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/utils/MockUtil.kt
new file mode 100644
index 000000000..1147bb74e
--- /dev/null
+++ b/PermissionController/tests/mocking/src/com/android/permissioncontroller/tests/mocking/utils/MockUtil.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.tests.mocking.utils
+
+import android.app.AppOpsManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import org.mockito.Mockito
+import org.mockito.Mockito.`when` as whenever
+
+object MockUtil {
+ fun createMockPackageOps(
+ packageName: String,
+ ops: List<AppOpsManager.OpEntry>,
+ uid: Int
+ ): AppOpsManager.PackageOps {
+ val pkgOp = Mockito.mock(AppOpsManager.PackageOps::class.java)
+ whenever(pkgOp.packageName).thenReturn(packageName)
+ whenever(pkgOp.ops).thenReturn(ops)
+ whenever(pkgOp.uid).thenReturn(uid)
+ return pkgOp
+ }
+
+ fun createOpEntry(opStr: String, time: Long): AppOpsManager.OpEntry {
+ val opEntry = Mockito.mock(AppOpsManager.OpEntry::class.java)
+ whenever(opEntry.opStr).thenReturn(opStr)
+ whenever(opEntry.getLastAccessTime(Mockito.anyInt())).thenReturn(time)
+ return opEntry
+ }
+
+ fun createPackageInfo(
+ testPackageName: String,
+ requestedPerms: List<String>,
+ requestedFlags: List<Int>,
+ applicationInfoFlags: Int = 0
+ ): PackageInfo {
+ val appInfo = ApplicationInfo()
+ appInfo.flags = applicationInfoFlags
+ return PackageInfo().apply {
+ packageName = testPackageName
+ requestedPermissions = requestedPerms.toTypedArray()
+ requestedPermissionsFlags = requestedFlags.toIntArray()
+ applicationInfo = appInfo
+ }
+ }
+}
diff --git a/PermissionController/tests/outofprocess/Android.bp b/PermissionController/tests/outofprocess/Android.bp
index e646def28..83bb65d6b 100644
--- a/PermissionController/tests/outofprocess/Android.bp
+++ b/PermissionController/tests/outofprocess/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
@@ -33,7 +34,6 @@ android_test {
srcs: [
"src/**/*.kt",
- ":permissioncontroller-protos",
],
libs: [
@@ -42,11 +42,11 @@ android_test {
],
static_libs: [
+ "permissioncontroller-protos",
"androidx.test.rules",
"androidx.test.ext.truth",
"androidx.test.ext.junit",
"compatibility-device-util-axt",
- "libprotobuf-java-lite",
],
proto: {
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..667a307ae 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
@@ -20,14 +20,17 @@ import android.os.ParcelFileDescriptor.AutoCloseInputStream
import android.os.UserHandle.myUserId
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.modules.utils.build.SdkLevel
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.assumeFalse
import org.junit.Assume.assumeTrue
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import java.nio.charset.StandardCharsets.UTF_8
@RunWith(AndroidJUnit4::class)
class DumpTest {
@@ -36,8 +39,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 {
@@ -48,6 +51,12 @@ class DumpTest {
}
}
+ @Before
+ fun setUp() {
+ // We no longer dump auto revoke data since T.
+ assumeFalse(SdkLevel.isAtLeastT())
+ }
+
@Test
fun autoRevokeDumpHasCurrentUser() {
val dump = getDump()
@@ -66,6 +75,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..6aa0dc45c 100644
--- a/PermissionController/tests/permissionui/Android.bp
+++ b/PermissionController/tests/permissionui/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
@@ -60,9 +61,12 @@ android_test {
":CtsAppThatRequestsLocationPermission29",
":PermissionUiUseStoragePermissionApp",
":PermissionUiUseCameraPermissionApp",
+ ":PermissionUiUseHealthConnectPermissionApp",
+ ":PermissionUiInvalidUseHealthConnectPermissionApp",
":PermissionUiDefineAdditionalPermissionApp",
":PermissionUiUseAdditionalPermissionApp",
":PermissionUiUseTwoAdditionalPermissionsApp",
+ ":PermissionUiReadCalendarPermissionApp",
],
per_testcase_directory: true,
}
diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml
index 62fc6ed3f..566410777 100644
--- a/PermissionController/tests/permissionui/AndroidTest.xml
+++ b/PermissionController/tests/permissionui/AndroidTest.xml
@@ -31,24 +31,30 @@
<!-- Create place to store apks -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="mkdir -p /data/local/tmp/permissioncontroller/tests/permissionui" />
- <option name="teardown-command" value="rm -rf /data/local/tmp/permissioncontroller/"/>
+ <option name="run-command" value="mkdir -p /data/local/tmp/pc-permissionui" />
+ <option name="teardown-command" value="rm -fr /data/local/tmp/pc-permissionui"/>
</target_preparer>
<!-- Load additional APKs onto device -->
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="push-file" key="CtsAppThatRequestsLocationPermission29.apk"
- value="/data/local/tmp/permissioncontroller/tests/permissionui/AppThatRequestsLocation.apk" />
+ value="/data/local/tmp/pc-permissionui/AppThatRequestsLocation.apk" />
<option name="push-file" key="PermissionUiUseStoragePermissionApp.apk"
- value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseStoragePermissionApp.apk" />
+ value="/data/local/tmp/pc-permissionui/PermissionUiUseStoragePermissionApp.apk" />
<option name="push-file" key="PermissionUiUseCameraPermissionApp.apk"
- value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseCameraPermissionApp.apk" />
+ value="/data/local/tmp/pc-permissionui/PermissionUiUseCameraPermissionApp.apk" />
+ <option name="push-file" key="PermissionUiUseHealthConnectPermissionApp.apk"
+ value="/data/local/tmp/pc-permissionui/PermissionUiUseHealthConnectPermissionApp.apk" />
+ <option name="push-file" key="PermissionUiInvalidUseHealthConnectPermissionApp.apk"
+ value="/data/local/tmp/pc-permissionui/PermissionUiInvalidUseHealthConnectPermissionApp.apk" />
<option name="push-file" key="PermissionUiDefineAdditionalPermissionApp.apk"
- value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiDefineAdditionalPermissionApp.apk" />
+ value="/data/local/tmp/pc-permissionui/PermissionUiDefineAdditionalPermissionApp.apk" />
<option name="push-file" key="PermissionUiUseAdditionalPermissionApp.apk"
- value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseAdditionalPermissionApp.apk" />
+ value="/data/local/tmp/pc-permissionui/PermissionUiUseAdditionalPermissionApp.apk" />
<option name="push-file" key="PermissionUiUseTwoAdditionalPermissionsApp.apk"
- value="/data/local/tmp/permissioncontroller/tests/permissionui/PermissionUiUseTwoAdditionalPermissionsApp.apk" />
+ value="/data/local/tmp/pc-permissionui/PermissionUiUseTwoAdditionalPermissionsApp.apk" />
+ <option name="push-file" key="PermissionUiReadCalendarPermissionApp.apk"
+ value="/data/local/tmp/pc-permissionui/PermissionUiReadCalendarPermissionApp.apk" />
</target_preparer>
<!-- Uninstall test-apps -->
diff --git a/PermissionController/tests/permissionui/PermissionUiDefineAdditionalPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiDefineAdditionalPermissionApp/Android.bp
index 13205b354..e93bb995f 100644
--- a/PermissionController/tests/permissionui/PermissionUiDefineAdditionalPermissionApp/Android.bp
+++ b/PermissionController/tests/permissionui/PermissionUiDefineAdditionalPermissionApp/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
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/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml
new file mode 100644
index 000000000..c5e0f4906
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiInvalidUseHealthConnectPermissionApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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" />
+</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/PermissionUiReadCalendarPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/Android.bp
new file mode 100644
index 000000000..edb0da6fc
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/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: "PermissionUiReadCalendarPermissionApp",
+
+ srcs: ["src/**/*.kt"],
+
+ sdk_version: "30",
+}
diff --git a/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml
new file mode 100644
index 000000000..8ebb502e8
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.permissioncontroller.tests.appthatrequestpermission">
+ <uses-permission android:name="android.permission.READ_CALENDAR"/>
+
+ <attribution android:tag="testTag" android:label="@string/test_attribution_label" />
+
+ <application android:label="CalendarRequestApp" />
+</manifest>
+
diff --git a/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml
new file mode 100644
index 000000000..91cb8c1fd
--- /dev/null
+++ b/PermissionController/tests/permissionui/PermissionUiReadCalendarPermissionApp/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Placeholder attribution label for testing [CHAR LIMIT=32] -->
+ <string name="test_attribution_label">Test Attribution Label</string>
+</resources>
diff --git a/PermissionController/tests/permissionui/PermissionUiUseAdditionalPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiUseAdditionalPermissionApp/Android.bp
index 7073b02cc..5fc26a3f0 100644
--- a/PermissionController/tests/permissionui/PermissionUiUseAdditionalPermissionApp/Android.bp
+++ b/PermissionController/tests/permissionui/PermissionUiUseAdditionalPermissionApp/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
diff --git a/PermissionController/tests/permissionui/PermissionUiUseCameraPermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiUseCameraPermissionApp/Android.bp
index 71616bd83..80252c891 100644
--- a/PermissionController/tests/permissionui/PermissionUiUseCameraPermissionApp/Android.bp
+++ b/PermissionController/tests/permissionui/PermissionUiUseCameraPermissionApp/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
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/PermissionUiUseStoragePermissionApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiUseStoragePermissionApp/Android.bp
index 56022ca2d..fe7be2ee2 100644
--- a/PermissionController/tests/permissionui/PermissionUiUseStoragePermissionApp/Android.bp
+++ b/PermissionController/tests/permissionui/PermissionUiUseStoragePermissionApp/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
diff --git a/PermissionController/tests/permissionui/PermissionUiUseTwoAdditionalPermissionsApp/Android.bp b/PermissionController/tests/permissionui/PermissionUiUseTwoAdditionalPermissionsApp/Android.bp
index aeff19c2d..a681f51bb 100644
--- a/PermissionController/tests/permissionui/PermissionUiUseTwoAdditionalPermissionsApp/Android.bp
+++ b/PermissionController/tests/permissionui/PermissionUiUseTwoAdditionalPermissionsApp/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
// 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"
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..ec846d778 100644
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/PermissionHub2Test.kt
@@ -25,78 +25,49 @@ import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
import android.os.Process.myUserHandle
-import android.provider.DeviceConfig
-import android.provider.DeviceConfig.NAMESPACE_PRIVACY
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil.eventually
-import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
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"
private val instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context = instrumentation.targetContext
- companion object {
- private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"
-
- private var wasPermissionHubEnabled = false
-
- @JvmStatic
- @BeforeClass
- fun enablePermissionHub2() {
-
- runWithShellPermissionIdentity {
- wasPermissionHubEnabled = DeviceConfig.getBoolean(NAMESPACE_PRIVACY,
- PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
- }
-
- if (!wasPermissionHubEnabled) {
- runShellCommand(
- "device_config put privacy $PROPERTY_PERMISSIONS_HUB_2_ENABLED true")
- }
- }
-
- @JvmStatic
- @AfterClass
- fun disablePermissionHub2() {
- if (!wasPermissionHubEnabled) {
- runShellCommand(
- "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..6234f6e17 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,23 +37,18 @@ 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() {
private val ONE_PERMISSION_DEFINER_APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiDefineAdditionalPermissionApp.apk"
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiDefineAdditionalPermissionApp.apk"
private val PERMISSION_USER_APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiUseAdditionalPermissionApp.apk"
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiUseAdditionalPermissionApp.apk"
private val TWO_PERMISSION_USER_APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiUseTwoAdditionalPermissionsApp.apk"
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiUseTwoAdditionalPermissionsApp.apk"
private val DEFINER_PKG = "com.android.permissioncontroller.tests.appthatdefinespermission"
private val USER_PKG = "com.android.permissioncontroller.tests.appthatrequestpermission"
@@ -62,8 +57,7 @@ class AllAppPermissionsFragmentTest : BasePermissionUiTest() {
private val TIMEOUT_SHORT = 500L
- @Before
- fun assumeNotTelevision() = assumeFalse(isTelevision)
+ @Before fun assumeNotTelevision() = assumeFalse(isTelevision)
@Before
fun wakeScreenUp() {
@@ -76,12 +70,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 +93,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..06fb4beda 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
@@ -31,17 +31,16 @@ import org.junit.After
import org.junit.Assert.assertNull
import org.junit.Assume.assumeFalse
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
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 +55,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 +71,31 @@ 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
+ @Ignore("b/301001789")
fun activityIsClosedWhenUserIsUninstalled() {
uninstallApp(USER_PKG)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS)))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS))) }
}
@Test
+ @Ignore("b/301001789")
fun activityIsClosedWhenDefinerIsUninstalled() {
uninstallApp(DEFINER_PKG)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS)))
- }
+ eventually { assertNull(waitFindObjectOrNull(By.text(APP_PERMISSIONS))) }
}
@After
@@ -105,4 +103,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..ff3631ea9 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,22 @@ 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/pc-permissionui" + "/PermissionUiUseAdditionalPermissionApp.apk",
+ "com.android.permissioncontroller.tests.appthatrequestpermission",
+ "com.android.permissioncontroller.tests.A",
+ "/data/local/tmp/pc-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..0f4f3841a
--- /dev/null
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAllAppPermissionFragmentTest.kt
@@ -0,0 +1,148 @@
+/*
+ * 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 androidx.test.uiautomator.Until
+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() {
+ uiDevice.performActionAndWait(
+ {
+ 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)
+ }
+ )
+ }
+ },
+ Until.newWindow(),
+ TIMEOUT_SHORT
+ )
+
+ 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..04dbac39f
--- /dev/null
+++ b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/HealthConnectAppPermissionFragmentTest.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.waitUntilObjectGone
+import com.android.permissioncontroller.permissionui.wakeUpScreen
+import org.junit.After
+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()
+
+ waitUntilObjectGone(By.text(HEALTH_CONNECT_LABEL), TIMEOUT_SHORT)
+ }
+
+ @Test
+ fun invalidGrantedUsedHealthConnectPermissionsAreListed() {
+ installInvalidTestAppThatUsesHealthConnectPermission()
+ grantTestAppPermission(HEALTH_CONNECT_PERMISSION_READ_FLOORS_CLIMBED)
+
+ startManageAppPermissionsActivity()
+
+ eventually { waitFindObject(By.text(HEALTH_CONNECT_LABEL)) }
+ }
+
+ private fun startManageAppPermissionsActivity() {
+ runWithShellPermissionIdentity {
+ instrumentationContext.startActivity(
+ 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..fb13c99ed 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
@@ -17,18 +17,24 @@
package com.android.permissioncontroller.permissionui.ui
import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.os.Build
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
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.
+ * TODO(b/304152870): On Android R, the package name never disappears from screen after uninstall
*/
@RunWith(AndroidJUnit4::class)
-class LocationPermissionAppsFragmentTest : PermissionAppsFragmentTest(
- "/data/local/tmp/permissioncontroller/tests/permissionui/AppThatRequestsLocation.apk",
- "android.permission.cts.appthatrequestpermission",
- ACCESS_COARSE_LOCATION
-)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+class LocationPermissionAppsFragmentTest :
+ PermissionAppsFragmentTest(
+ "/data/local/tmp/pc-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..276494ab3 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
@@ -19,14 +19,17 @@ package com.android.permissioncontroller.permissionui.ui
import android.content.Intent
import android.permission.cts.PermissionUtils.install
import android.permission.cts.PermissionUtils.uninstallApp
+import android.util.Log
import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
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 com.google.common.truth.Truth.assertWithMessage
import org.junit.After
-import org.junit.Assert.assertNull
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
@@ -37,6 +40,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(
@@ -46,61 +50,98 @@ abstract class PermissionAppsFragmentTest(
val definerApk: String? = null,
val definerPkg: String? = null
) : BasePermissionUiTest() {
+ val pkgSelector = By.text(userPkg)
+ var startTimeMillis: Long = 0
- @Before
- fun assumeNotTelevision() = assumeFalse(isTelevision)
-
- @Before
- fun wakeScreenUp() {
- wakeUpScreen()
+ private fun scrollFindFromTop(selector: BySelector): UiObject2? {
+ val scrollable = uiDevice.findObject(By.scrollable(true))
+ if (scrollable != null) {
+ logCheckPoint("found scrollable, so proceeding to scroll")
+ logCheckPoint("starting scroll up")
+ scrollable.scrollUntil(Direction.UP, Until.scrollFinished(Direction.UP))
+ logCheckPoint("finished scroll up")
+ logCheckPoint("starting scroll down")
+ var uiObject = scrollable.scrollUntil(Direction.DOWN, Until.findObject(selector))
+ logCheckPoint("finished scroll down")
+ return uiObject
+ }
+ logCheckPoint("no scrollable on screen, so finding object directly")
+ return uiDevice.findObject(selector)
}
@Before
- fun installDefinerApk() {
+ fun setUp() {
+ startTimeMillis = System.currentTimeMillis()
+ logCheckPoint("setUp: started")
+ assumeFalse(isTelevision)
+ wakeUpScreen()
if (definerApk != null) {
install(definerApk)
}
- }
-
- @Before
- fun startManagePermissionAppsActivity() {
- runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
- .apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- putExtra(Intent.EXTRA_PERMISSION_NAME, perm)
- })
- }
+ uninstallApp(userPkg)
+ uiDevice.performActionAndWait(
+ {
+ runWithShellPermissionIdentity {
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_PERMISSION_APPS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(Intent.EXTRA_PERMISSION_NAME, perm)
+ }
+ )
+ }
+ },
+ Until.newWindow(),
+ NEW_WINDOW_TIMEOUT_MILLIS
+ )
+ logCheckPoint("setUp: finished")
}
@Test
- fun appAppearsWhenInstalled() {
- assertNull(waitFindObjectOrNull(By.text(userPkg)))
+ fun testAppAppearanceReflectsInstallation() {
+ logCheckPoint("testAppAppearanceReflectsInstallation: started")
+ // Install package
install(userApk)
- eventually {
- waitFindObject(By.text(userPkg))
- }
- }
-
- // TODO(b/280652042) Slow tests aren't good
- @Test(timeout = 120000)
- fun appDisappearsWhenUninstalled() {
- assertNull(waitFindObjectOrNull(By.text(userPkg)))
+ logCheckPoint("installed app")
- install(userApk)
- eventually {
- waitFindObject(By.text(userPkg))
- }
+ // Expect *to* find package listed on screen
+ eventually(
+ {
+ val pkg = scrollFindFromTop(pkgSelector)
+ assertWithMessage(
+ "Package '$userPkg' was NOT visible after installing, but should be"
+ )
+ .that(pkg)
+ .isNotNull()
+ },
+ Companion.SCROLL_TIMEOUT_MILLIS
+ )
+ // Uninstall app
uninstallApp(userPkg)
- eventually {
- assertNull(waitFindObjectOrNull(By.text(userPkg)))
- }
+ logCheckPoint("uninstalled app")
+
+ // Expect *not to* find package listed on screen
+ eventually(
+ {
+ val pkg = scrollFindFromTop(pkgSelector)
+ assertWithMessage(
+ "Package '$userPkg' was visible after uninstalling, but should NOT be"
+ )
+ .that(pkg)
+ .isNull()
+ },
+ Companion.SCROLL_TIMEOUT_MILLIS
+ )
+
+ logCheckPoint("confirmed installed app shown on screen")
+ logCheckPoint("testAppAppearanceReflectsInstallation: finished")
}
@After
fun tearDown() {
+ logCheckPoint("tearDown: started")
if (definerPkg != null) {
uninstallApp(definerPkg)
}
@@ -108,5 +149,17 @@ abstract class PermissionAppsFragmentTest(
uiDevice.pressBack()
uiDevice.pressHome()
+ logCheckPoint("tearDown: finished")
+ }
+
+ fun logCheckPoint(logMessage: String) {
+ val elapsedSeconds = (System.currentTimeMillis() - startTimeMillis) / 1000
+ Log.v(TAG, "(${elapsedSeconds}s): $logMessage")
+ }
+
+ companion object {
+ const val NEW_WINDOW_TIMEOUT_MILLIS = 25_000L
+ const val SCROLL_TIMEOUT_MILLIS = 25_000L
+ val TAG = PermissionAppsFragmentTest::class.java.simpleName
}
}
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..3994a3ae6 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
@@ -17,19 +17,24 @@
package com.android.permissioncontroller.permissionui.ui
import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.os.Build
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
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.
+ * TODO(b/304152870): On Android R, the package name never disappears from screen after uninstall
*/
@RunWith(AndroidJUnit4::class)
-class StoragePermissionAppsFragmentTest : PermissionAppsFragmentTest(
- "/data/local/tmp/permissioncontroller/tests/permissionui" +
- "/PermissionUiUseStoragePermissionApp.apk",
- "com.android.permissioncontroller.tests.appthatrequestpermission",
- READ_EXTERNAL_STORAGE
-)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+class StoragePermissionAppsFragmentTest :
+ PermissionAppsFragmentTest(
+ "/data/local/tmp/pc-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..09f59de30 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
@@ -21,9 +21,13 @@ import android.permission.cts.PermissionUtils.install
import android.permission.cts.PermissionUtils.uninstallApp
// Test Apps' APK files
-private const val APK_DIRECTORY = "/data/local/tmp/permissioncontroller/tests/permissionui/"
+private const val APK_DIRECTORY = "/data/local/tmp/pc-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..eb7be564b 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,17 +34,13 @@ 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 =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiDefineAdditionalPermissionApp.apk"
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiDefineAdditionalPermissionApp.apk"
private val PERMISSION_USER_APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiUseAdditionalPermissionApp.apk"
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiUseAdditionalPermissionApp.apk"
private val DEFINER_PKG = "com.android.permissioncontroller.tests.appthatdefinespermission"
private val USER_PKG = "com.android.permissioncontroller.tests.appthatrequestpermission"
@@ -57,9 +53,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 +86,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..1ad876245 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
@@ -18,12 +18,14 @@ package com.android.permissioncontroller.permissionui.ui.handheld
import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.content.Intent
+import android.content.pm.PackageManager
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 android.util.Log
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,28 +38,51 @@ 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 =
- "/data/local/tmp/permissioncontroller/tests/permissionui/AppThatRequestsLocation.apk"
- private val ADDITIONAL_DEFINER_APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiDefineAdditionalPermissionApp.apk"
- private val ADDITIONAL_USER_APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui/" +
- "PermissionUiUseAdditionalPermissionApp.apk"
- private val LOCATION_USER_PKG = "android.permission.cts.appthatrequestpermission"
- private val ADDITIONAL_DEFINER_PKG =
- "com.android.permissioncontroller.tests.appthatdefinespermission"
- private val ADDITIONAL_USER_PKG =
- "com.android.permissioncontroller.tests.appthatrequestpermission"
- private val ADDITIONAL_PERMISSIONS_LABEL = "Additional permissions"
- private val ADDITIONAL_PERMISSIONS_SUMMARY = "more"
-
- private val locationGroupLabel = "Location"
+ @Before
+ fun setup() {
+ wakeUpScreen()
+
+ runWithShellPermissionIdentity {
+ removePackageIfInstalled()
+ instrumentationContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ )
+ }
+ }
+
+ @After
+ fun tearDown() {
+ uninstallApp(LOCATION_USER_PKG)
+ uninstallApp(ADDITIONAL_DEFINER_PKG)
+ uninstallApp(ADDITIONAL_USER_PKG)
+ uiDevice.pressBack()
+ }
+
+ /**
+ * The test packages are not expected to be installed already, remove them if they are already
+ * installed (i.e. leftover from another test) when a test starts.
+ */
+ private fun removePackageIfInstalled() {
+ val packageNames = listOf(LOCATION_USER_PKG, ADDITIONAL_DEFINER_PKG, ADDITIONAL_USER_PKG)
+ for (packageName in packageNames) {
+ try {
+ val packageInfo =
+ instrumentationContext.packageManager.getPackageInfo(packageName, 0)
+ if (packageInfo != null) {
+ Log.w(LOG_TAG, "package $packageName not expected to be installed.")
+ uninstallApp(packageName)
+ Thread.sleep(1000)
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ // ignore
+ }
+ }
+ }
/**
* Read the number of additional permissions from the Ui.
@@ -68,96 +93,89 @@ 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()
}
}
- @Before
- fun setup() {
- wakeUpScreen()
-
- runWithShellPermissionIdentity {
- instrumentationContext.startActivity(Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- })
- }
-
- reuninstallApp(LOCATION_USER_APK, LOCATION_USER_PKG)
-
- // Sleep before each test for 1 second for getUsageCountsFromUi to get the correct counts
- Thread.sleep(1000)
- }
-
@Test
fun groupSummaryGetsUpdatedWhenAppGetsInstalled() {
- val original = getUsageCountsFromUi(locationGroupLabel)
+ val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
install(LOCATION_USER_APK)
- eventually {
- val afterInstall = getUsageCountsFromUi(locationGroupLabel)
- assertThat(afterInstall.granted).isEqualTo(original.granted)
- assertThat(afterInstall.total).isEqualTo(original.total + 1)
- }
+ eventually(
+ {
+ val afterInstall = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
+ assertThat(afterInstall.granted).isEqualTo(original.granted)
+ assertThat(afterInstall.total).isEqualTo(original.total + 1)
+ },
+ TIMEOUT
+ )
}
@Test
fun groupSummaryGetsUpdatedWhenAppGetsUninstalled() {
- reuninstallApp(LOCATION_USER_APK, LOCATION_USER_PKG)
- val original = getUsageCountsFromUi(locationGroupLabel)
-
+ val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
install(LOCATION_USER_APK)
- eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel)).isNotEqualTo(original)
- }
+ eventually(
+ { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isNotEqualTo(original) },
+ TIMEOUT
+ )
uninstallApp(LOCATION_USER_PKG)
- reuninstallApp(LOCATION_USER_APK, LOCATION_USER_PKG)
- eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel)).isEqualTo(original)
- }
+ eventually(
+ { assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL)).isEqualTo(original) },
+ TIMEOUT
+ )
}
@Test
fun groupSummaryGetsUpdatedWhenPermissionGetsGranted() {
- val original = getUsageCountsFromUi(locationGroupLabel)
+ val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
install(LOCATION_USER_APK)
- eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).total)
- .isEqualTo(original.total + 1)
- }
+ eventually(
+ {
+ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).total)
+ .isEqualTo(original.total + 1)
+ },
+ TIMEOUT
+ )
grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).granted)
+ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
.isEqualTo(original.granted + 1)
}
}
@Test
fun groupSummaryGetsUpdatedWhenPermissionGetsRevoked() {
- val original = getUsageCountsFromUi(locationGroupLabel)
+ val original = getUsageCountsFromUi(LOCATION_GROUP_LABEL)
install(LOCATION_USER_APK)
grantPermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
- eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).total)
- .isNotEqualTo(original.total)
- assertThat(getUsageCountsFromUi(locationGroupLabel).granted)
- .isNotEqualTo(original.granted)
- }
+ eventually(
+ {
+ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).total)
+ .isNotEqualTo(original.total)
+ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
+ .isNotEqualTo(original.granted)
+ },
+ TIMEOUT
+ )
revokePermission(LOCATION_USER_PKG, ACCESS_COARSE_LOCATION)
eventually {
- assertThat(getUsageCountsFromUi(locationGroupLabel).granted)
+ assertThat(getUsageCountsFromUi(LOCATION_GROUP_LABEL).granted)
.isEqualTo(original.granted)
}
}
@@ -168,10 +186,12 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_DEFINER_APK)
install(ADDITIONAL_USER_APK)
- eventually {
- assertThat(getAdditionalPermissionCount())
- .isEqualTo(additionalPermissionBefore + 1)
- }
+ eventually(
+ {
+ assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore + 1)
+ },
+ TIMEOUT
+ )
}
@Test
@@ -180,15 +200,16 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_DEFINER_APK)
install(ADDITIONAL_USER_APK)
- eventually {
- assertThat(getAdditionalPermissionCount())
- .isNotEqualTo(additionalPermissionBefore)
- }
+ eventually(
+ { assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) },
+ TIMEOUT
+ )
uninstallApp(ADDITIONAL_USER_PKG)
- eventually {
- assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore)
- }
+ eventually(
+ { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) },
+ TIMEOUT
+ )
}
@Test
@@ -197,32 +218,38 @@ class ManageStandardPermissionsFragmentTest : BaseHandheldPermissionUiTest() {
install(ADDITIONAL_DEFINER_APK)
install(ADDITIONAL_USER_APK)
- eventually {
- assertThat(getAdditionalPermissionCount())
- .isNotEqualTo(additionalPermissionBefore)
- }
+ eventually(
+ { assertThat(getAdditionalPermissionCount()).isNotEqualTo(additionalPermissionBefore) },
+ TIMEOUT
+ )
uninstallApp(ADDITIONAL_DEFINER_PKG)
- eventually {
- assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore)
- }
- }
-
- fun reuninstallApp(apk: String, pkg: String) {
- // b/275752754: sometimes this test is flaky because inside @After it didn't uninstall the
- // packages correctly. This caused the permission manager screen to still show the package
- // as requesting the permission and caused the original usage count to be incorrect.
- // Installing/uninstalling the packages again to ensure the package is correctly uninstalled
- install(apk)
- uninstallApp(pkg)
+ eventually(
+ { assertThat(getAdditionalPermissionCount()).isEqualTo(additionalPermissionBefore) },
+ TIMEOUT
+ )
}
- @After
- fun tearDown() {
- uninstallApp(LOCATION_USER_PKG)
- uninstallApp(ADDITIONAL_DEFINER_PKG)
- uninstallApp(ADDITIONAL_USER_PKG)
-
- uiDevice.pressBack()
+ companion object {
+ private val LOG_TAG = ManageStandardPermissionsFragmentTest::class.java.simpleName
+
+ private const val LOCATION_USER_APK =
+ "/data/local/tmp/pc-permissionui/AppThatRequestsLocation.apk"
+ private const val ADDITIONAL_DEFINER_APK =
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiDefineAdditionalPermissionApp.apk"
+ private const val ADDITIONAL_USER_APK =
+ "/data/local/tmp/pc-permissionui/" + "PermissionUiUseAdditionalPermissionApp.apk"
+ private const val LOCATION_USER_PKG = "android.permission.cts.appthatrequestpermission"
+ private const val ADDITIONAL_DEFINER_PKG =
+ "com.android.permissioncontroller.tests.appthatdefinespermission"
+ private const val ADDITIONAL_USER_PKG =
+ "com.android.permissioncontroller.tests.appthatrequestpermission"
+ private const val ADDITIONAL_PERMISSIONS_LABEL = "Additional permissions"
+ private const val ADDITIONAL_PERMISSIONS_SUMMARY = "more"
+ private const val LOCATION_GROUP_LABEL = "Location"
+
+ // Package Added/Removed broadcast are pretty slow on cf devices, we may want to increase
+ // this in future if the test still fails.
+ private const val TIMEOUT = 30000L
}
}
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..f81b620ce 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
@@ -40,13 +40,10 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
class PermissionUsageFragmentTest : PermissionHub2Test() {
- private val APK =
- "/data/local/tmp/permissioncontroller/tests/permissionui" +
- "/PermissionUiUseCameraPermissionApp.apk"
+ private val APK = "/data/local/tmp/pc-permissionui" + "/PermissionUiUseCameraPermissionApp.apk"
private val APP = "com.android.permissioncontroller.tests.appthatrequestpermission"
private val APP_LABEL = "CameraRequestApp"
private val CAMERA_PREF_LABEL = "Camera"
- private val REFRESH = "Refresh"
@Before
fun setup() {
@@ -62,19 +59,15 @@ 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 {
- try {
- waitFindObject(By.res("android:id/title").textContains(CAMERA_PREF_LABEL)).click()
- } catch (e: Exception) {
- waitFindObject(By.textContains(REFRESH)).click()
- throw e
- }
+ waitFindObject(By.res("android:id/title").textContains(CAMERA_PREF_LABEL)).click()
}
-
waitFindObject(By.textContains(APP_LABEL))
}
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..9f0f548e0 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
@@ -22,6 +22,7 @@ import com.android.permissioncontroller.permissionui.ui.TEST_APP_DEFINED_PERMISS
import com.android.permissioncontroller.permissionui.ui.grantTestAppPermission
import com.android.permissioncontroller.permissionui.ui.installTestAppThatDefinesAdditionalPermissions
import com.android.permissioncontroller.permissionui.ui.installTestAppThatUsesAdditionalPermission
+import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -29,10 +30,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 +43,32 @@ 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
+ )
+ }
+
+ @After
+ fun goOutOfOtherPermissionsScreen() {
+ uiDevice.pressBack()
}
@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 +82,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/OWNERS b/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/OWNERS
deleted file mode 100644
index d9f93959b..000000000
--- a/PermissionController/tests/permissionui/src/com/android/permissioncontroller/permissionui/ui/television/OWNERS
+++ /dev/null
@@ -1,10 +0,0 @@
-# Primary maintainer
-sergeynv@google.com
-
-# Other maintainers (ATV Core Framework team)
-bronger@google.com
-galinap@google.com
-philipjunker@google.com
-rgl@google.com
-robhor@google.com
-valiiftime@google.com
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/Annotations/Android.bp b/SafetyCenter/Annotations/Android.bp
index 852d734b0..12d2c0fb0 100644
--- a/SafetyCenter/Annotations/Android.bp
+++ b/SafetyCenter/Annotations/Android.bp
@@ -16,6 +16,7 @@
// Library for internal data used by Safety Center system service and UI.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -32,7 +33,6 @@ java_library {
":safetycenter-annotations-sources",
],
libs: [
- "androidx.annotation_annotation",
"jsr305",
],
apex_available: [
diff --git a/SafetyCenter/Config/Android.bp b/SafetyCenter/Config/Android.bp
index 895b242b0..a8d275226 100644
--- a/SafetyCenter/Config/Android.bp
+++ b/SafetyCenter/Config/Android.bp
@@ -14,6 +14,7 @@
// limitations under the License.
//
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -39,6 +40,7 @@ java_library {
],
static_libs: [
"modules-utils-build",
+ "com.android.permission.flags-aconfig-java",
],
apex_available: [
"com.android.permission",
diff --git a/SafetyCenter/Config/TEST_MAPPING b/SafetyCenter/Config/TEST_MAPPING
index c35d3d777..1ad65fddd 100644
--- a/SafetyCenter/Config/TEST_MAPPING
+++ b/SafetyCenter/Config/TEST_MAPPING
@@ -9,7 +9,17 @@
"name": "SafetyCenterConfigTests[com.google.android.permission.apex]",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "SafetyCenterConfigTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
index b6730f36f..38eee9e51 100644
--- a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
+++ b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java
@@ -33,10 +33,12 @@ import android.content.res.Resources;
import android.safetycenter.config.SafetyCenterConfig;
import android.safetycenter.config.SafetySource;
import android.safetycenter.config.SafetySourcesGroup;
+import android.util.Log;
import androidx.annotation.RequiresApi;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.flags.Flags;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -49,6 +51,7 @@ import java.io.InputStream;
@RequiresApi(TIRAMISU)
public final class SafetyCenterConfigParser {
+ private static final String TAG = "SafetyCenterConfigParser";
private static final String TAG_SAFETY_CENTER_CONFIG = "safety-center-config";
private static final String TAG_SAFETY_SOURCES_CONFIG = "safety-sources-config";
private static final String TAG_SAFETY_SOURCES_GROUP = "safety-sources-group";
@@ -64,6 +67,8 @@ public final class SafetyCenterConfigParser {
private static final String ATTR_SAFETY_SOURCE_PACKAGE_NAME = "packageName";
private static final String ATTR_SAFETY_SOURCE_TITLE = "title";
private static final String ATTR_SAFETY_SOURCE_TITLE_FOR_WORK = "titleForWork";
+ private static final String ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE =
+ "titleForPrivateProfile";
private static final String ATTR_SAFETY_SOURCE_SUMMARY = "summary";
private static final String ATTR_SAFETY_SOURCE_INTENT_ACTION = "intentAction";
private static final String ATTR_SAFETY_SOURCE_PROFILE = "profile";
@@ -270,6 +275,26 @@ public final class SafetyCenterConfigParser {
parser.getAttributeName(i),
resources));
break;
+ case ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE:
+ if (SdkLevel.isAtLeastV()) {
+ if (Flags.privateProfileTitleApi()) {
+ builder.setTitleForPrivateProfileResId(
+ parseStringResourceName(
+ parser.getAttributeValue(i),
+ name,
+ parser.getAttributeName(i),
+ resources));
+ } else {
+ Log.i(
+ TAG,
+ String.format(
+ "Ignoring attribute %s.%s",
+ name, ATTR_SAFETY_SOURCE_TITLE_FOR_PRIVATE_PROFILE));
+ }
+ break;
+ } else {
+ throw attributeUnexpected(name, parser.getAttributeName(i));
+ }
case ATTR_SAFETY_SOURCE_SUMMARY:
builder.setSummaryResId(
parseStringResourceName(
diff --git a/SafetyCenter/Config/tests/Android.bp b/SafetyCenter/Config/tests/Android.bp
index c58f2c922..f80dec61e 100644
--- a/SafetyCenter/Config/tests/Android.bp
+++ b/SafetyCenter/Config/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -32,6 +33,7 @@ android_test {
"compatibility-device-util-axt",
"safety-center-config",
"safety-center-test-util-lib",
+ "com.android.permission.flags-aconfig-java",
],
test_suites: [
"general-tests",
diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
index 64775d7fe..e0e16fd71 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt
@@ -19,6 +19,7 @@ package com.android.safetycenter.config
import android.content.Context
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import com.android.modules.utils.build.SdkLevel
+import com.android.permission.flags.Flags
import com.android.safetycenter.config.tests.R
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
@@ -63,18 +64,39 @@ class ParserConfigInvalidTest {
fun parameters() =
arrayOf(
Params(
+ "ConfigDynamicSafetySourceAllDisabledNoPrivate",
+ R.raw.config_dynamic_safety_source_all_disabled_no_private,
+ "Element dynamic-safety-source invalid",
+ "Required attribute titleForPrivateProfile missing",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigDynamicSafetySourceAllDisabledNoWork",
R.raw.config_dynamic_safety_source_all_disabled_no_work,
"Element dynamic-safety-source invalid",
"Required attribute titleForWork missing"
),
Params(
+ "ConfigDynamicSafetySourceAllHiddenWithSearchNoPrivate",
+ R.raw.config_dynamic_safety_source_all_hidden_with_search_no_private,
+ "Element dynamic-safety-source invalid",
+ "Required attribute titleForPrivateProfile missing",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigDynamicSafetySourceAllHiddenWithSearchNoWork",
R.raw.config_dynamic_safety_source_all_hidden_with_search_no_work,
"Element dynamic-safety-source invalid",
"Required attribute titleForWork missing"
),
Params(
+ "ConfigDynamicSafetySourceAllNoPrivate",
+ R.raw.config_dynamic_safety_source_all_no_private,
+ "Element dynamic-safety-source invalid",
+ "Required attribute titleForPrivateProfile missing",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigDynamicSafetySourceAllNoWork",
R.raw.config_dynamic_safety_source_all_no_work,
"Element dynamic-safety-source invalid",
@@ -160,12 +182,26 @@ class ParserConfigInvalidTest {
"Required attribute title missing"
),
Params(
+ "ConfigDynamicSafetySourcePrimaryHiddenWithPrivate",
+ R.raw.config_dynamic_safety_source_primary_hidden_with_private,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute titleForPrivateProfile present",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigDynamicSafetySourcePrimaryHiddenWithWork",
R.raw.config_dynamic_safety_source_primary_hidden_with_work,
"Element dynamic-safety-source invalid",
"Prohibited attribute titleForWork present"
),
Params(
+ "ConfigDynamicSafetySourcePrimaryWithPrivate",
+ R.raw.config_dynamic_safety_source_primary_with_private,
+ "Element dynamic-safety-source invalid",
+ "Prohibited attribute titleForPrivateProfile present",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigDynamicSafetySourcePrimaryWithWork",
R.raw.config_dynamic_safety_source_primary_with_work,
"Element dynamic-safety-source invalid",
@@ -226,6 +262,13 @@ class ParserConfigInvalidTest {
"Prohibited attribute intentAction present"
),
Params(
+ "ConfigIssueOnlySafetySourceWithPrivate",
+ R.raw.config_issue_only_safety_source_with_private,
+ "Element issue-only-safety-source invalid",
+ "Prohibited attribute titleForPrivateProfile present",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigIssueOnlySafetySourceWithSearch",
R.raw.config_issue_only_safety_source_with_search,
"Element issue-only-safety-source invalid",
@@ -425,12 +468,26 @@ class ParserConfigInvalidTest {
SdkLevel.isAtLeastU()
),
Params(
+ "ConfigStaticSafetySourceWithPrimaryAndPrivate",
+ R.raw.config_static_safety_source_with_primary_and_private,
+ "Element static-safety-source invalid",
+ "Prohibited attribute titleForPrivateProfile present",
+ SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()
+ ),
+ Params(
"ConfigStaticSafetySourceWithPrimaryAndWork",
R.raw.config_static_safety_source_with_primary_and_work,
"Element static-safety-source invalid",
"Prohibited attribute titleForWork present"
),
Params(
+ "ConfigStaticSafetySourceWithPrivatePreV",
+ R.raw.config_static_safety_source_with_private_profile,
+ "Unexpected attribute static-safety-source.titleForPrivateProfile",
+ null,
+ !SdkLevel.isAtLeastV()
+ ),
+ Params(
"ConfigStaticSafetySourceWithRefresh",
R.raw.config_static_safety_source_with_refresh,
"Element static-safety-source invalid",
diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
index b63ccead7..133f4ce73 100644
--- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
+++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt
@@ -23,6 +23,7 @@ import android.safetycenter.config.SafetySourcesGroup
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.modules.utils.build.SdkLevel
+import com.android.permission.flags.Flags
import com.android.safetycenter.config.tests.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -77,6 +78,9 @@ class ParserConfigValidTest {
addPackageCertificateHash("feed1")
addPackageCertificateHash("feed2")
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(R.string.reference)
+ }
}
.build()
)
@@ -101,6 +105,9 @@ class ParserConfigValidTest {
addPackageCertificateHash("feed1")
addPackageCertificateHash("feed2")
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(R.string.reference)
+ }
}
.build()
)
@@ -133,6 +140,11 @@ class ParserConfigValidTest {
.setProfile(SafetySource.PROFILE_ALL)
.setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
.setSearchTermsResId(R.string.reference)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(R.string.reference)
+ }
+ }
.build()
)
.build()
@@ -159,6 +171,11 @@ class ParserConfigValidTest {
.setIntentAction("intent")
.setProfile(SafetySource.PROFILE_ALL)
.setSearchTermsResId(R.string.reference)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(R.string.reference)
+ }
+ }
.build()
)
.build()
diff --git a/SafetyCenter/Config/tests/overlay/Android.bp b/SafetyCenter/Config/tests/overlay/Android.bp
index 65eab02f1..a6f9fb2dd 100644
--- a/SafetyCenter/Config/tests/overlay/Android.bp
+++ b/SafetyCenter/Config/tests/overlay/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml
new file mode 100644
index 000000000..dae6ca754
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_disabled_no_private.xml
@@ -0,0 +1,18 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ initialDisplayState="disabled"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml
new file mode 100644
index 000000000..e1852b6ec
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_hidden_with_search_no_private.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ profile="all_profiles"
+ initialDisplayState="hidden"
+ searchTerms="@com.android.safetycenter.config.tests:string/reference"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml
new file mode 100644
index 000000000..8446b71cc
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_all_no_private.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="all_profiles"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml
new file mode 100644
index 000000000..3d5840b03
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_hidden_with_private.xml
@@ -0,0 +1,15 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml
new file mode 100644
index 000000000..b95a3ed6e
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_dynamic_safety_source_primary_with_private.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <dynamic-safety-source
+ id="id"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml
new file mode 100644
index 000000000..7b2484a41
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_issue_only_safety_source_with_private.xml
@@ -0,0 +1,12 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id">
+ <issue-only-safety-source
+ id="id"
+ packageName="package"
+ profile="all_profiles"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml b/SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml
new file mode 100644
index 000000000..0ab3d885b
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_static_safety_source_with_primary_and_private.xml
@@ -0,0 +1,16 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <static-safety-source
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw-v35/config_valid.xml b/SafetyCenter/Config/tests/res/raw-v35/config_valid.xml
new file mode 100644
index 000000000..e49a6cdc4
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw-v35/config_valid.xml
@@ -0,0 +1,197 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="dynamic"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ statelessIconType="privacy">
+ <dynamic-safety-source
+ id="dynamic_barebone"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ <dynamic-safety-source
+ id="dynamic_all_optional"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ initialDisplayState="disabled"
+ maxSeverityLevel="300"
+ searchTerms="@com.android.safetycenter.config.tests:string/reference"
+ loggingAllowed="false"
+ refreshOnPageOpenAllowed="true"
+ notificationsAllowed="true"
+ deduplicationGroup="group"
+ packageCertificateHashes="feed1,feed2"/>
+ <dynamic-safety-source
+ id="@com.android.safetycenter.config.tests:string/dynamic_all_references_id"
+ packageName="@com.android.safetycenter.config.tests:string/dynamic_all_references_package_name"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="@com.android.safetycenter.config.tests:string/dynamic_all_references_intent_action"
+ profile="@com.android.safetycenter.config.tests:string/dynamic_all_references_profile"
+ initialDisplayState="@com.android.safetycenter.config.tests:string/dynamic_all_references_initial_display_state"
+ maxSeverityLevel="@com.android.safetycenter.config.tests:string/dynamic_all_references_max_severity_level"
+ searchTerms="@com.android.safetycenter.config.tests:string/reference"
+ loggingAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_logging_allowed"
+ refreshOnPageOpenAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_refresh_on_page_open_allowed"
+ notificationsAllowed="@com.android.safetycenter.config.tests:string/dynamic_all_references_notifications_allowed"
+ deduplicationGroup="@com.android.safetycenter.config.tests:string/dynamic_all_references_deduplication_group"
+ packageCertificateHashes="@com.android.safetycenter.config.tests:string/dynamic_all_references_package_cert_hashes"/>
+ <dynamic-safety-source
+ id="dynamic_disabled"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ profile="primary_profile_only"
+ initialDisplayState="disabled"/>
+ <dynamic-safety-source
+ id="dynamic_hidden"
+ packageName="package"
+ profile="all_profiles"
+ initialDisplayState="hidden"/>
+ <dynamic-safety-source
+ id="dynamic_hidden_with_search"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ initialDisplayState="hidden"
+ searchTerms="@com.android.safetycenter.config.tests:string/reference"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="static"
+ title="@com.android.safetycenter.config.tests:string/reference">
+ <static-safety-source
+ id="static_barebone"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ <static-safety-source
+ id="static_all_optional"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="all_profiles"
+ searchTerms="@com.android.safetycenter.config.tests:string/reference"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="issue_only">
+ <issue-only-safety-source
+ id="issue_only_barebone"
+ packageName="package"
+ profile="primary_profile_only"/>
+ <issue-only-safety-source
+ id="issue_only_all_optional"
+ packageName="package"
+ profile="all_profiles"
+ maxSeverityLevel="300"
+ loggingAllowed="false"
+ refreshOnPageOpenAllowed="true"
+ notificationsAllowed="true"
+ deduplicationGroup="group"
+ packageCertificateHashes="feed1,feed2"/>
+ <issue-only-safety-source
+ id="id_test_abcxyz_ABCXYZ_012789"
+ packageName="package"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="mixed"
+ title="@com.android.safetycenter.config.tests:string/reference">
+ <dynamic-safety-source
+ id="mixed_dynamic_barebone"
+ packageName="package"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ <issue-only-safety-source
+ id="mixed_issue_only_barebone"
+ packageName="package"
+ profile="primary_profile_only"/>
+ <static-safety-source
+ id="mixed_static_barebone"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ type="stateful"
+ id="stateful_barebone"
+ title="@com.android.safetycenter.config.tests:string/reference">
+ <static-safety-source
+ id="stateful_barebone_source"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ type="stateful"
+ id="stateful_all_optional"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ statelessIconType="privacy">
+ <static-safety-source
+ id="stateful_all_optional_source"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ type="stateless"
+ id="stateless_barebone"
+ title="@com.android.safetycenter.config.tests:string/reference">
+ <static-safety-source
+ id="stateless_barebone_source"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ type="stateless"
+ id="stateless_all_optional"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ statelessIconType="privacy">
+ <static-safety-source
+ id="stateless_all_optional_source"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ type="hidden"
+ id="hidden_barebone">
+ <issue-only-safety-source
+ id="hidden_barebone_source"
+ packageName="package"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ <safety-sources-group
+ type="hidden"
+ id="hidden_all_optional"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ statelessIconType="privacy">
+ <issue-only-safety-source
+ id="hidden_all_optional_source"
+ packageName="package"
+ profile="primary_profile_only"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml b/SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml
new file mode 100644
index 000000000..f790baec9
--- /dev/null
+++ b/SafetyCenter/Config/tests/res/raw/config_static_safety_source_with_private_profile.xml
@@ -0,0 +1,17 @@
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference">
+ <static-safety-source
+ id="id"
+ title="@com.android.safetycenter.config.tests:string/reference"
+ titleForWork="@com.android.safetycenter.config.tests:string/reference"
+ titleForPrivateProfile="@com.android.safetycenter.config.tests:string/reference"
+ summary="@com.android.safetycenter.config.tests:string/reference"
+ intentAction="intent"
+ profile="all_profiles"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/ConfigLintChecker/Android.bp b/SafetyCenter/ConfigLintChecker/Android.bp
index ff1e28009..7c615d2f4 100644
--- a/SafetyCenter/ConfigLintChecker/Android.bp
+++ b/SafetyCenter/ConfigLintChecker/Android.bp
@@ -12,63 +12,65 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
+// TODO(b/322944911): Reconsider enabling linter checker
+//package {
+// default_team: "trendy_team_android_permissions",
+// default_applicable_licenses: ["Android-Apache-2.0"],
+//}
-java_library_host {
- name: "ConfigLintChecker",
- srcs: [
- "java/**/*.java",
- "java/**/*.kt",
- ":safetycenter-annotations-sources",
- ":safetycenter-config-api-sources",
- ":safetycenter-config-parser-sources",
- ],
- plugins: ["auto_service_plugin"],
- libs: [
- "androidx.annotation_annotation", // For androidx.annotation.RequiresApi
- "auto_service_annotations",
- "core-xml-for-host", // For org.xmlpull.v1.*
- "framework-annotations-lib", // For com.android.annotation.*
- "layoutlib_api-prebuilt", // For com.android.resources.ResourceFolderType
- "lint_api",
- ],
- java_resources: [":safetycenter-config-schemas"],
- jarjar_rules: "jarjar-rules.txt",
- kotlincflags: ["-Xjvm-default=all"],
- visibility: [
- "//packages/modules/Permission:__subpackages__",
- "//vendor:__subpackages__",
- ],
-}
+//java_library_host {
+// name: "ConfigLintChecker",
+// srcs: [
+// "java/**/*.java",
+// "java/**/*.kt",
+// ":safetycenter-annotations-sources",
+// ":safetycenter-config-api-sources",
+// ":safetycenter-config-parser-sources",
+// ],
+// plugins: ["auto_service_plugin"],
+// libs: [
+// "androidx.annotation_annotation", // For androidx.annotation.RequiresApi
+// "auto_service_annotations",
+// "core-xml-for-host", // For org.xmlpull.v1.*
+// "framework-annotations-lib", // For com.android.annotation.*
+// "layoutlib_api-prebuilt", // For com.android.resources.ResourceFolderType
+// "lint_api",
+// ],
+// java_resources: [":safetycenter-config-schemas"],
+// jarjar_rules: "jarjar-rules.txt",
+// kotlincflags: ["-Xjvm-default=all"],
+// visibility: [
+// "//packages/modules/Permission:__subpackages__",
+// "//vendor:__subpackages__",
+// ],
+//}
-java_test_host {
- name: "ConfigLintCheckerTest",
- srcs: [
- "tests/java/**/*.kt",
- ],
- static_libs: [
- "ConfigLintChecker",
- "junit",
- "lint",
- "lint_tests",
- ],
- test_options: {
- unit_test: true,
- tradefed_options: [
- {
- // lint bundles in some classes that were built with older versions
- // of libraries, and no longer load. Since tradefed tries to load
- // all classes in the jar to look for tests, it crashes loading them.
- // Exclude these classes from tradefed's search.
- name: "exclude-paths",
- value: "org/apache",
- },
- {
- name: "exclude-paths",
- value: "META-INF",
- },
- ],
- },
-}
+//java_test_host {
+// name: "ConfigLintCheckerTest",
+// srcs: [
+// "tests/java/**/*.kt",
+// ],
+// static_libs: [
+// "ConfigLintChecker",
+// "junit",
+// "lint",
+// "lint_tests",
+// ],
+// test_options: {
+// unit_test: true,
+// tradefed_options: [
+// {
+// // lint bundles in some classes that were built with older versions
+// // of libraries, and no longer load. Since tradefed tries to load
+// // all classes in the jar to look for tests, it crashes loading them.
+// // Exclude these classes from tradefed's search.
+// name: "exclude-paths",
+// value: "org/apache",
+// },
+// {
+// name: "exclude-paths",
+// value: "META-INF",
+// },
+// ],
+// },
+//}
diff --git a/SafetyCenter/ConfigLintChecker/java/android/os/Build.java b/SafetyCenter/ConfigLintChecker/java/android/os/Build.java
index 531b6e481..4a58e04fc 100644
--- a/SafetyCenter/ConfigLintChecker/java/android/os/Build.java
+++ b/SafetyCenter/ConfigLintChecker/java/android/os/Build.java
@@ -24,7 +24,11 @@ public final class Build {
public static final class VERSION_CODES {
/** Constant used in the Safety Center config code. */
public static final int TIRAMISU = 33;
+
/** Constant used in the Safety Center config code. */
public static final int UPSIDE_DOWN_CAKE = 34;
+
+ /** Constant used in the Safety Center config code. */
+ public static final int VANILLA_ICE_CREAM = 35;
}
}
diff --git a/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java b/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java
index dbfaa56b1..cfe99071c 100644
--- a/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java
+++ b/SafetyCenter/ConfigLintChecker/java/com/android/modules/utils/build/SdkLevel.java
@@ -18,6 +18,7 @@ package com.android.modules.utils.build;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM;
/** Stub class to compile the linter for host execution. */
public final class SdkLevel {
@@ -42,4 +43,9 @@ public final class SdkLevel {
public static boolean isAtLeastU() {
return sSdkInt >= UPSIDE_DOWN_CAKE;
}
+
+ /** Method used in the Safety Center config code. */
+ public static boolean isAtLeastV() {
+ return sSdkInt >= VANILLA_ICE_CREAM;
+ }
}
diff --git a/SafetyCenter/InternalData/Android.bp b/SafetyCenter/InternalData/Android.bp
index aa8f96c8b..c89db4962 100644
--- a/SafetyCenter/InternalData/Android.bp
+++ b/SafetyCenter/InternalData/Android.bp
@@ -16,6 +16,7 @@
// Library for internal data used by Safety Center system service and UI.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/SafetyCenter/PendingIntents/Android.bp b/SafetyCenter/PendingIntents/Android.bp
index 05bec7988..e9b810df8 100644
--- a/SafetyCenter/PendingIntents/Android.bp
+++ b/SafetyCenter/PendingIntents/Android.bp
@@ -14,6 +14,7 @@
// limitations under the License.
//
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/SafetyCenter/Persistence/Android.bp b/SafetyCenter/Persistence/Android.bp
index 56ce4491b..d7e1a60dc 100644
--- a/SafetyCenter/Persistence/Android.bp
+++ b/SafetyCenter/Persistence/Android.bp
@@ -14,6 +14,7 @@
// limitations under the License.
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/SafetyCenter/Persistence/tests/Android.bp b/SafetyCenter/Persistence/tests/Android.bp
index d72dfdd29..c3342ab18 100644
--- a/SafetyCenter/Persistence/tests/Android.bp
+++ b/SafetyCenter/Persistence/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/SafetyCenter/Resources/Android.bp b/SafetyCenter/Resources/Android.bp
index 06f7e5f25..a10ea7f1a 100644
--- a/SafetyCenter/Resources/Android.bp
+++ b/SafetyCenter/Resources/Android.bp
@@ -16,6 +16,7 @@
// APK to hold all the Safety Center overlayable resources.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -41,9 +42,10 @@ android_app {
min_sdk_version: "30",
apex_available: ["com.android.permission"],
certificate: ":com.android.safetycenter.resources.certificate",
- lint: {
- extra_check_modules: ["ConfigLintChecker"],
- },
+ // TODO(b/322944911): Reconsider enabling linter checker
+ //lint: {
+ // extra_check_modules: ["ConfigLintChecker"],
+ //},
static_libs: [
"SafetyCenterResourcesShared",
],
@@ -51,5 +53,5 @@ android_app {
android_app_certificate {
name: "com.android.safetycenter.resources.certificate",
- certificate: "com.android.safetycenter.resources"
+ certificate: "com.android.safetycenter.resources",
}
diff --git a/SafetyCenter/Resources/res/drawable-night-v35/illustration_android_cellular_network_security_sources.xml b/SafetyCenter/Resources/res/drawable-night-v35/illustration_android_cellular_network_security_sources.xml
new file mode 100644
index 000000000..0be096375
--- /dev/null
+++ b/SafetyCenter/Resources/res/drawable-night-v35/illustration_android_cellular_network_security_sources.xml
@@ -0,0 +1,18 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="276dp" android:viewportHeight="300" android:viewportWidth="412" android:width="379.04dp">
+ <path android:fillColor="#000000" android:pathData="M384.18,300H27.82C12.53,300 0,287.17 0,271.52V28.52C0,12.83 12.53,0 27.82,0H384.29C399.47,0 412,12.83 412,28.48V271.63C412,287.17 399.47,300 384.18,300Z"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M330.09,146.55C330.09,215.31 274.35,271.05 205.59,271.05C136.83,271.05 81.09,215.31 81.09,146.55C81.09,77.8 136.83,22.05 205.59,22.05C274.35,22.05 330.09,77.8 330.09,146.55Z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-96.5,0a96.5,96.5 0,1 1,193 0a96.5,96.5 0,1 1,-193 0" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-67.5,0a67.5,67.5 0,1 1,135 0a67.5,67.5 0,1 1,-135 0" android:strokeAlpha="0.2"/>
+ <path android:fillColor="#000000" android:pathData="M311.5,219.5m-45.5,0a45.5,45.5 0,1 1,91 0a45.5,45.5 0,1 1,-91 0"/>
+ <path android:fillColor="#000000" android:pathData="M311.5,261.31C334.8,261.31 353.69,242.47 353.69,219.22C353.69,195.98 334.8,177.14 311.5,177.14C288.2,177.14 269.31,195.98 269.31,219.22C269.31,242.47 288.2,261.31 311.5,261.31Z"/>
+ <path android:fillColor="#669DF6" android:pathData="M269.92,219.39C269.92,196.42 288.61,177.92 311.5,177.92C334.39,177.92 353.08,196.42 353.08,219.39C353.08,242.37 334.54,260.86 311.5,260.86C288.46,260.86 269.92,242.37 269.92,219.39ZM276.64,219.24C276.64,238.36 292.34,254.02 311.5,254.02C330.66,254.02 346.36,238.51 346.36,219.24C346.36,200.13 330.66,184.46 311.5,184.46C292.34,184.46 276.64,200.13 276.64,219.24Z" android:strokeColor="#000000" android:strokeWidth="1.84466"/>
+ <path android:fillColor="#1B6EF3" android:pathData="M317.38,219.1H308.27C303.67,219.1 298.33,215.12 298.33,209.54C298.33,204.54 301.02,202.1 304.1,200.58L296.78,203.2C295.34,203.71 294.39,205.07 294.39,206.59V216.24C294.44,217.89 294.55,219.48 294.77,221.13C295.81,228.13 299.79,236.08 310.68,241.63C311.54,242.07 312.58,242.07 313.44,241.63C318.11,239.26 321.49,236.44 323.94,233.44C325.05,231.85 325.84,230.05 325.84,227.93C325.84,222.16 321.86,219.1 317.38,219.1Z"/>
+ <path android:fillColor="#7CACF8" android:pathData="M327.35,203.2L313.3,198.13C312.5,197.85 311.64,197.85 310.85,198.13L304.08,200.58C301.01,202.12 298.32,204.57 298.32,209.54C298.32,215.12 303.64,219.1 308.26,219.1H317.36C321.87,219.1 325.83,222.16 325.83,227.93C325.83,230.05 325.05,231.85 323.93,233.44C327.18,229.43 328.75,225.14 329.34,221.13C329.56,219.5 329.7,217.89 329.72,216.24V206.59C329.72,205.07 328.77,203.73 327.33,203.2H327.35Z"/>
+ <path android:fillColor="#000000" android:pathData="M333.81,202.84C333.81,200.94 332.63,199.26 330.85,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L292.96,198.6C291.18,199.26 290,200.94 290,202.84V214.9C290.06,216.96 290.19,218.94 290.47,221.01C291.76,229.76 296.68,239.7 310.18,246.63C311.25,247.18 312.54,247.18 313.6,246.63C327.1,239.67 332.03,229.76 333.32,221.01C333.59,218.97 333.76,216.96 333.78,214.9V202.84H333.81Z"/>
+ <path android:fillColor="#0842A0" android:pathData="M322.72,235.96C322.86,235.32 323.65,230.97 321.32,226.6C320.01,224.15 318.31,222.72 315.98,220.87C314.4,219.63 310.18,216.91 309.11,216.25C309.17,216.27 309.25,216.36 309.3,216.38C308.73,216.03 308.76,216.03 309.11,216.25C307.93,215.45 305.91,213.85 304.38,211.1C302.54,207.82 302.48,204.77 302.48,204.19C302.48,204.13 302.48,204.11 302.48,204.05C302.48,200.36 303.64,197.5 305.47,195.44C306.84,193.9 308.4,193.01 309.71,192.49L292.96,198.6C291.18,199.24 290,200.94 290,202.84V214.9C290.05,216.96 290.19,218.95 290.46,221.01C291.75,229.76 296.68,239.7 310.18,246.64C311.25,247.19 312.53,247.19 313.6,246.64C314.86,246 316.01,245.32 317.11,244.63C318.8,243.36 321.79,240.55 322.69,235.96H322.72Z"/>
+ <path android:fillColor="#000000" android:pathData="M309.31,216.38C309.31,216.38 309.17,216.27 309.12,216.24C308.76,216.02 308.73,216.02 309.31,216.38Z"/>
+ <path android:fillColor="#467CF1" android:pathData="M330.86,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L309.72,192.52C308.43,193.04 306.84,193.92 305.47,195.46C303.64,197.53 302.49,200.39 302.49,204.08C302.49,204.13 302.49,204.16 302.49,204.21C302.49,204.79 302.57,207.85 304.38,211.13C305.91,213.88 307.91,215.45 309.11,216.27C310.18,216.96 314.4,219.66 315.99,220.9C318.32,222.74 320.04,224.17 321.33,226.62C323.65,230.97 322.83,235.35 322.72,235.98C321.82,240.58 318.83,243.36 317.14,244.65C327.98,237.88 332.17,228.96 333.35,221.03C333.62,219 333.79,216.99 333.81,214.92V202.84C333.81,200.94 332.64,199.26 330.86,198.6Z"/>
+ <path android:fillColor="#3C4043" android:pathData="M272.32,147.03V147.03C272.32,108.93 241.64,78 203.82,78C165.99,78 135.32,108.87 135.32,146.97C135.32,185.07 165.99,216 203.82,216C241.64,216 272.26,185.13 272.32,147.03ZM138.67,147.03C138.67,110.72 167.87,81.36 203.82,81.36C239.77,81.42 268.91,110.78 268.97,147.03C268.96,183.33 239.82,212.69 203.82,212.69C167.82,212.69 138.67,183.33 138.67,147.03Z" android:strokeColor="#3C4043" android:strokeWidth="2"/>
+ <path android:fillColor="#5BB974" android:pathData="M225.17,207.55L225.17,207.55L225.16,207.56C223.69,208.09 223.33,209.76 223.79,211.04C224.03,211.7 224.44,212.25 225.1,212.5C225.73,212.73 226.35,212.61 226.84,212.44C255.99,201.88 271.55,177.91 272.99,148.23L272.99,148.23L272.99,148.22L273,148.06C273.02,147.36 272.8,146.68 272.42,146.15C272.05,145.62 271.48,145.17 270.78,145.02C269.22,144.7 268.07,145.97 268.02,147.37C267.07,172.62 252.22,198.21 225.17,207.55Z" android:strokeColor="#5BB974" android:strokeWidth="2"/>
+ <path android:fillColor="#669DF6" android:pathData="M173.3,84.74L173.3,84.74L173.29,84.75C164.69,89.25 155.96,96.43 149.08,104.95C142.21,113.47 137.14,123.41 135.97,133.45L135.97,133.46C135.96,133.47 135.96,133.48 135.96,133.49C135.96,133.52 135.95,133.56 135.95,133.59L135.93,133.75L135.95,133.79C135.99,134.97 137.06,135.71 138.13,135.63C139.17,135.56 140.19,134.73 140.28,133.59C141.37,124.44 146.05,115.37 152.5,107.45C158.96,99.52 167.15,92.81 175.17,88.36C176.11,87.87 176.79,86.68 176.21,85.55C175.63,84.4 174.26,84.26 173.3,84.74Z" android:strokeColor="#669DF6" android:strokeWidth="2"/>
+</vector>
diff --git a/SafetyCenter/Resources/res/drawable-v35/illustration_android_cellular_network_security_sources.xml b/SafetyCenter/Resources/res/drawable-v35/illustration_android_cellular_network_security_sources.xml
new file mode 100644
index 000000000..a048a91c4
--- /dev/null
+++ b/SafetyCenter/Resources/res/drawable-v35/illustration_android_cellular_network_security_sources.xml
@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="276.69904dp" android:viewportHeight="300" android:viewportWidth="412" android:width="380dp">
+ <path android:fillColor="#ffffff" android:pathData="M384.18,300H27.82C12.53,300 0,287.17 0,271.52V28.52C0,12.83 12.53,0 27.82,0H384.29C399.47,0 412,12.83 412,28.48V271.63C412,287.17 399.47,300 384.18,300Z"/>
+ <path android:fillAlpha="0.2" android:fillColor="#1A73E8" android:pathData="M330.09,146.55C330.09,215.31 274.35,271.05 205.59,271.05C136.83,271.05 81.09,215.31 81.09,146.55C81.09,77.8 136.83,22.05 205.59,22.05C274.35,22.05 330.09,77.8 330.09,146.55Z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-96.5,0a96.5,96.5 0,1 1,193 0a96.5,96.5 0,1 1,-193 0" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-67.5,0a67.5,67.5 0,1 1,135 0a67.5,67.5 0,1 1,-135 0" android:strokeAlpha="0.2"/>
+ <path android:fillColor="#ffffff" android:pathData="M311.5,219.5m-45.5,0a45.5,45.5 0,1 1,91 0a45.5,45.5 0,1 1,-91 0"/>
+ <path android:fillColor="#ffffff" android:pathData="M311.5,261.31C334.8,261.31 353.69,242.47 353.69,219.22C353.69,195.98 334.8,177.14 311.5,177.14C288.2,177.14 269.31,195.98 269.31,219.22C269.31,242.47 288.2,261.31 311.5,261.31Z"/>
+ <path android:fillColor="#1A73E8" android:pathData="M269.92,219.39C269.92,196.42 288.61,177.92 311.5,177.92C334.39,177.92 353.08,196.42 353.08,219.39C353.08,242.37 334.54,260.86 311.5,260.86C288.46,260.86 269.92,242.37 269.92,219.39ZM276.64,219.24C276.64,238.36 292.34,254.02 311.5,254.02C330.66,254.02 346.36,238.51 346.36,219.24C346.36,200.13 330.66,184.46 311.5,184.46C292.34,184.46 276.64,200.13 276.64,219.24Z" android:strokeColor="#ffffff" android:strokeWidth="1.84466"/>
+ <path android:fillColor="#1B6EF3" android:pathData="M317.38,219.1H308.27C303.67,219.1 298.33,215.12 298.33,209.54C298.33,204.54 301.02,202.1 304.1,200.58L296.78,203.2C295.34,203.71 294.39,205.07 294.39,206.59V216.24C294.44,217.89 294.55,219.48 294.77,221.13C295.81,228.13 299.79,236.08 310.68,241.63C311.54,242.07 312.58,242.07 313.44,241.63C318.11,239.26 321.49,236.44 323.94,233.44C325.05,231.85 325.84,230.05 325.84,227.93C325.84,222.16 321.86,219.1 317.38,219.1Z"/>
+ <path android:fillColor="#7CACF8" android:pathData="M327.35,203.2L313.3,198.13C312.5,197.85 311.64,197.85 310.85,198.13L304.08,200.58C301.01,202.12 298.32,204.57 298.32,209.54C298.32,215.12 303.64,219.1 308.26,219.1H317.36C321.87,219.1 325.83,222.16 325.83,227.93C325.83,230.05 325.05,231.85 323.93,233.44C327.18,229.43 328.75,225.14 329.34,221.13C329.56,219.5 329.7,217.89 329.72,216.24V206.59C329.72,205.07 328.77,203.73 327.33,203.2H327.35Z"/>
+ <path android:fillColor="#ffffff" android:pathData="M333.81,202.84C333.81,200.94 332.63,199.26 330.85,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L292.96,198.6C291.18,199.26 290,200.94 290,202.84V214.9C290.06,216.96 290.19,218.94 290.47,221.01C291.76,229.76 296.68,239.7 310.18,246.63C311.25,247.18 312.54,247.18 313.6,246.63C327.1,239.67 332.03,229.76 333.32,221.01C333.59,218.97 333.76,216.96 333.78,214.9V202.84H333.81Z"/>
+ <path android:fillColor="#0842A0" android:pathData="M322.72,235.96C322.86,235.32 323.65,230.97 321.32,226.6C320.01,224.15 318.31,222.72 315.98,220.87C314.4,219.63 310.18,216.91 309.11,216.25C309.17,216.27 309.25,216.36 309.3,216.38C308.73,216.03 308.76,216.03 309.11,216.25C307.93,215.45 305.91,213.85 304.38,211.1C302.54,207.82 302.48,204.77 302.48,204.19C302.48,204.13 302.48,204.11 302.48,204.05C302.48,200.36 303.64,197.5 305.47,195.44C306.84,193.9 308.4,193.01 309.71,192.49L292.96,198.6C291.18,199.24 290,200.94 290,202.84V214.9C290.05,216.96 290.19,218.95 290.46,221.01C291.75,229.76 296.68,239.7 310.18,246.64C311.25,247.19 312.53,247.19 313.6,246.64C314.86,246 316.01,245.32 317.11,244.63C318.8,243.36 321.79,240.55 322.69,235.96H322.72Z"/>
+ <path android:fillColor="#ffffff" android:pathData="M309.31,216.38C309.31,216.38 309.17,216.27 309.12,216.24C308.76,216.02 308.73,216.02 309.31,216.38Z"/>
+ <path android:fillColor="#467CF1" android:pathData="M330.86,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L309.72,192.52C308.43,193.04 306.84,193.92 305.47,195.46C303.64,197.53 302.49,200.39 302.49,204.08C302.49,204.13 302.49,204.16 302.49,204.21C302.49,204.79 302.57,207.85 304.38,211.13C305.91,213.88 307.91,215.45 309.11,216.27C310.18,216.96 314.4,219.66 315.99,220.9C318.32,222.74 320.04,224.17 321.33,226.62C323.65,230.97 322.83,235.35 322.72,235.98C321.82,240.58 318.83,243.36 317.14,244.65C327.98,237.88 332.17,228.96 333.35,221.03C333.62,219 333.79,216.99 333.81,214.92V202.84C333.81,200.94 332.64,199.26 330.86,198.6Z"/>
+ <path android:fillColor="#1A73E8" android:pathData="M169.57,177V178H170.57H183.32H184.32V177V151.5V150.5H183.32H170.57H169.57V151.5V177ZM195.07,177V178H196.07H208.82H209.82V177V130.25V129.25H208.82H196.07H195.07V130.25V177ZM220.57,177V178H221.57H234.32H235.32V177V109V108H234.32H221.57H220.57V109V177Z" android:strokeColor="#1A73E8" android:strokeWidth="2"/>
+ <path android:fillColor="#E8EAED" android:pathData="M272.32,147.03V147.03C272.32,108.93 241.64,78 203.82,78C165.99,78 135.32,108.87 135.32,146.97C135.32,185.07 165.99,216 203.82,216C241.64,216 272.26,185.13 272.32,147.03ZM138.67,147.03C138.67,110.72 167.87,81.36 203.82,81.36C239.77,81.42 268.91,110.78 268.97,147.03C268.96,183.33 239.82,212.69 203.82,212.69C167.82,212.69 138.67,183.33 138.67,147.03Z" android:strokeColor="#E8EAED" android:strokeWidth="2"/>
+ <path android:fillColor="#1E8E3E" android:pathData="M225.17,207.55L225.17,207.55L225.16,207.56C223.69,208.09 223.33,209.76 223.79,211.04C224.03,211.7 224.44,212.25 225.1,212.5C225.73,212.73 226.35,212.61 226.84,212.44C255.99,201.88 271.55,177.91 272.99,148.23L272.99,148.23L272.99,148.22L273,148.06C273.02,147.36 272.8,146.68 272.42,146.15C272.05,145.62 271.48,145.17 270.78,145.02C269.22,144.7 268.07,145.97 268.02,147.37C267.07,172.62 252.22,198.21 225.17,207.55Z" android:strokeColor="#1E8E3E" android:strokeWidth="2"/>
+ <path android:fillColor="#1A73E8" android:pathData="M173.3,84.74L173.3,84.74L173.29,84.75C164.69,89.25 155.96,96.43 149.08,104.95C142.21,113.47 137.14,123.41 135.97,133.45L135.97,133.46C135.96,133.47 135.96,133.48 135.96,133.49C135.96,133.52 135.95,133.56 135.95,133.59L135.93,133.75L135.95,133.79C135.99,134.97 137.06,135.71 138.13,135.63C139.17,135.56 140.19,134.73 140.28,133.59C141.37,124.44 146.05,115.37 152.5,107.45C158.96,99.52 167.15,92.81 175.17,88.36C176.11,87.87 176.79,86.68 176.21,85.55C175.63,84.4 174.26,84.26 173.3,84.74Z" android:strokeColor="#1A73E8" android:strokeWidth="2"/>
+</vector>
diff --git a/SafetyCenter/Resources/res/drawable-w480dp-night-v35/illustration_android_cellular_network_security_sources.xml b/SafetyCenter/Resources/res/drawable-w480dp-night-v35/illustration_android_cellular_network_security_sources.xml
new file mode 100644
index 000000000..db7846f75
--- /dev/null
+++ b/SafetyCenter/Resources/res/drawable-w480dp-night-v35/illustration_android_cellular_network_security_sources.xml
@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="615dp" android:viewportHeight="300" android:viewportWidth="412" android:width="844.6dp">
+ <path android:fillColor="#000000" android:pathData="M384.18,300H27.82C12.53,300 0,287.17 0,271.52V28.52C0,12.83 12.53,0 27.82,0H384.29C399.47,0 412,12.83 412,28.48V271.63C412,287.17 399.47,300 384.18,300Z"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M330.09,146.55C330.09,215.31 274.35,271.05 205.59,271.05C136.83,271.05 81.09,215.31 81.09,146.55C81.09,77.8 136.83,22.05 205.59,22.05C274.35,22.05 330.09,77.8 330.09,146.55Z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-96.5,0a96.5,96.5 0,1 1,193 0a96.5,96.5 0,1 1,-193 0" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-67.5,0a67.5,67.5 0,1 1,135 0a67.5,67.5 0,1 1,-135 0" android:strokeAlpha="0.2"/>
+ <path android:fillColor="#000000" android:pathData="M311.5,219.5m-45.5,0a45.5,45.5 0,1 1,91 0a45.5,45.5 0,1 1,-91 0"/>
+ <path android:fillColor="#000000" android:pathData="M311.5,261.31C334.8,261.31 353.69,242.47 353.69,219.22C353.69,195.98 334.8,177.14 311.5,177.14C288.2,177.14 269.31,195.98 269.31,219.22C269.31,242.47 288.2,261.31 311.5,261.31Z"/>
+ <path android:fillColor="#669DF6" android:pathData="M269.92,219.39C269.92,196.42 288.61,177.92 311.5,177.92C334.39,177.92 353.08,196.42 353.08,219.39C353.08,242.37 334.54,260.86 311.5,260.86C288.46,260.86 269.92,242.37 269.92,219.39ZM276.64,219.24C276.64,238.36 292.34,254.02 311.5,254.02C330.66,254.02 346.36,238.51 346.36,219.24C346.36,200.13 330.66,184.46 311.5,184.46C292.34,184.46 276.64,200.13 276.64,219.24Z" android:strokeColor="#000000" android:strokeWidth="1.84466"/>
+ <path android:fillColor="#1B6EF3" android:pathData="M317.38,219.1H308.27C303.67,219.1 298.33,215.12 298.33,209.54C298.33,204.54 301.02,202.1 304.1,200.58L296.78,203.2C295.34,203.71 294.39,205.07 294.39,206.59V216.24C294.44,217.89 294.55,219.48 294.77,221.13C295.81,228.13 299.79,236.08 310.68,241.63C311.54,242.07 312.58,242.07 313.44,241.63C318.11,239.26 321.49,236.44 323.94,233.44C325.05,231.85 325.84,230.05 325.84,227.93C325.84,222.16 321.86,219.1 317.38,219.1Z"/>
+ <path android:fillColor="#7CACF8" android:pathData="M327.35,203.2L313.3,198.13C312.5,197.85 311.64,197.85 310.85,198.13L304.08,200.58C301.01,202.12 298.32,204.57 298.32,209.54C298.32,215.12 303.64,219.1 308.26,219.1H317.36C321.87,219.1 325.83,222.16 325.83,227.93C325.83,230.05 325.05,231.85 323.93,233.44C327.18,229.43 328.75,225.14 329.34,221.13C329.56,219.5 329.7,217.89 329.72,216.24V206.59C329.72,205.07 328.77,203.73 327.33,203.2H327.35Z"/>
+ <path android:fillColor="#000000" android:pathData="M333.81,202.84C333.81,200.94 332.63,199.26 330.85,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L292.96,198.6C291.18,199.26 290,200.94 290,202.84V214.9C290.06,216.96 290.19,218.94 290.47,221.01C291.76,229.76 296.68,239.7 310.18,246.63C311.25,247.18 312.54,247.18 313.6,246.63C327.1,239.67 332.03,229.76 333.32,221.01C333.59,218.97 333.76,216.96 333.78,214.9V202.84H333.81Z"/>
+ <path android:fillColor="#0842A0" android:pathData="M322.72,235.96C322.86,235.32 323.65,230.97 321.32,226.6C320.01,224.15 318.31,222.72 315.98,220.87C314.4,219.63 310.18,216.91 309.11,216.25C309.17,216.27 309.25,216.36 309.3,216.38C308.73,216.03 308.76,216.03 309.11,216.25C307.93,215.45 305.91,213.85 304.38,211.1C302.54,207.82 302.48,204.77 302.48,204.19C302.48,204.13 302.48,204.11 302.48,204.05C302.48,200.36 303.64,197.5 305.47,195.44C306.84,193.9 308.4,193.01 309.71,192.49L292.96,198.6C291.18,199.24 290,200.94 290,202.84V214.9C290.05,216.96 290.19,218.95 290.46,221.01C291.75,229.76 296.68,239.7 310.18,246.64C311.25,247.19 312.53,247.19 313.6,246.64C314.86,246 316.01,245.32 317.11,244.63C318.8,243.36 321.79,240.55 322.69,235.96H322.72Z"/>
+ <path android:fillColor="#000000" android:pathData="M309.31,216.38C309.31,216.38 309.17,216.27 309.12,216.24C308.76,216.02 308.73,216.02 309.31,216.38Z"/>
+ <path android:fillColor="#467CF1" android:pathData="M330.86,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L309.72,192.52C308.43,193.04 306.84,193.92 305.47,195.46C303.64,197.53 302.49,200.39 302.49,204.08C302.49,204.13 302.49,204.16 302.49,204.21C302.49,204.79 302.57,207.85 304.38,211.13C305.91,213.88 307.91,215.45 309.11,216.27C310.18,216.96 314.4,219.66 315.99,220.9C318.32,222.74 320.04,224.17 321.33,226.62C323.65,230.97 322.83,235.35 322.72,235.98C321.82,240.58 318.83,243.36 317.14,244.65C327.98,237.88 332.17,228.96 333.35,221.03C333.62,219 333.79,216.99 333.81,214.92V202.84C333.81,200.94 332.64,199.26 330.86,198.6Z"/>
+ <path android:fillColor="#D2E3FC" android:pathData="M169.57,177V178H170.57H183.32H184.32V177V151.5V150.5H183.32H170.57H169.57V151.5V177ZM195.07,177V178H196.07H208.82H209.82V177V130.25V129.25H208.82H196.07H195.07V130.25V177ZM220.57,177V178H221.57H234.32H235.32V177V109V108H234.32H221.57H220.57V109V177Z" android:strokeColor="#D2E3FC" android:strokeWidth="2"/>
+ <path android:fillColor="#3C4043" android:pathData="M272.32,147.03V147.03C272.32,108.93 241.64,78 203.82,78C165.99,78 135.32,108.87 135.32,146.97C135.32,185.07 165.99,216 203.82,216C241.64,216 272.26,185.13 272.32,147.03ZM138.67,147.03C138.67,110.72 167.87,81.36 203.82,81.36C239.77,81.42 268.91,110.78 268.97,147.03C268.96,183.33 239.82,212.69 203.82,212.69C167.82,212.69 138.67,183.33 138.67,147.03Z" android:strokeColor="#3C4043" android:strokeWidth="2"/>
+ <path android:fillColor="#5BB974" android:pathData="M225.17,207.55L225.17,207.55L225.16,207.56C223.69,208.09 223.33,209.76 223.79,211.04C224.03,211.7 224.44,212.25 225.1,212.5C225.73,212.73 226.35,212.61 226.84,212.44C255.99,201.88 271.55,177.91 272.99,148.23L272.99,148.23L272.99,148.22L273,148.06C273.02,147.36 272.8,146.68 272.42,146.15C272.05,145.62 271.48,145.17 270.78,145.02C269.22,144.7 268.07,145.97 268.02,147.37C267.07,172.62 252.22,198.21 225.17,207.55Z" android:strokeColor="#5BB974" android:strokeWidth="2"/>
+ <path android:fillColor="#669DF6" android:pathData="M173.3,84.74L173.3,84.74L173.29,84.75C164.69,89.25 155.96,96.43 149.08,104.95C142.21,113.47 137.14,123.41 135.97,133.45L135.97,133.46C135.96,133.47 135.96,133.48 135.96,133.49C135.96,133.52 135.95,133.56 135.95,133.59L135.93,133.75L135.95,133.79C135.99,134.97 137.06,135.71 138.13,135.63C139.17,135.56 140.19,134.73 140.28,133.59C141.37,124.44 146.05,115.37 152.5,107.45C158.96,99.52 167.15,92.81 175.17,88.36C176.11,87.87 176.79,86.68 176.21,85.55C175.63,84.4 174.26,84.26 173.3,84.74Z" android:strokeColor="#669DF6" android:strokeWidth="2"/>
+</vector>
diff --git a/SafetyCenter/Resources/res/drawable-w480dp-v35/illustration_android_cellular_network_security_sources.xml b/SafetyCenter/Resources/res/drawable-w480dp-v35/illustration_android_cellular_network_security_sources.xml
new file mode 100644
index 000000000..85d9229f8
--- /dev/null
+++ b/SafetyCenter/Resources/res/drawable-w480dp-v35/illustration_android_cellular_network_security_sources.xml
@@ -0,0 +1,19 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="614.5631dp" android:viewportHeight="300" android:viewportWidth="412" android:width="844dp">
+ <path android:fillColor="#ffffff" android:pathData="M384.18,300H27.82C12.53,300 0,287.17 0,271.52V28.52C0,12.83 12.53,0 27.82,0H384.29C399.47,0 412,12.83 412,28.48V271.63C412,287.17 399.47,300 384.18,300Z"/>
+ <path android:fillAlpha="0.2" android:fillColor="#1A73E8" android:pathData="M330.09,146.55C330.09,215.31 274.35,271.05 205.59,271.05C136.83,271.05 81.09,215.31 81.09,146.55C81.09,77.8 136.83,22.05 205.59,22.05C274.35,22.05 330.09,77.8 330.09,146.55Z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-96.5,0a96.5,96.5 0,1 1,193 0a96.5,96.5 0,1 1,-193 0" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#669DF6" android:pathData="M205.5,146.5m-67.5,0a67.5,67.5 0,1 1,135 0a67.5,67.5 0,1 1,-135 0" android:strokeAlpha="0.2"/>
+ <path android:fillColor="#ffffff" android:pathData="M311.5,219.5m-45.5,0a45.5,45.5 0,1 1,91 0a45.5,45.5 0,1 1,-91 0"/>
+ <path android:fillColor="#ffffff" android:pathData="M311.5,261.31C334.8,261.31 353.69,242.47 353.69,219.22C353.69,195.98 334.8,177.14 311.5,177.14C288.2,177.14 269.31,195.98 269.31,219.22C269.31,242.47 288.2,261.31 311.5,261.31Z"/>
+ <path android:fillColor="#1A73E8" android:pathData="M269.92,219.39C269.92,196.42 288.61,177.92 311.5,177.92C334.39,177.92 353.08,196.42 353.08,219.39C353.08,242.37 334.54,260.86 311.5,260.86C288.46,260.86 269.92,242.37 269.92,219.39ZM276.64,219.24C276.64,238.36 292.34,254.02 311.5,254.02C330.66,254.02 346.36,238.51 346.36,219.24C346.36,200.13 330.66,184.46 311.5,184.46C292.34,184.46 276.64,200.13 276.64,219.24Z" android:strokeColor="#ffffff" android:strokeWidth="1.84466"/>
+ <path android:fillColor="#1B6EF3" android:pathData="M317.38,219.1H308.27C303.67,219.1 298.33,215.12 298.33,209.54C298.33,204.54 301.02,202.1 304.1,200.58L296.78,203.2C295.34,203.71 294.39,205.07 294.39,206.59V216.24C294.44,217.89 294.55,219.48 294.77,221.13C295.81,228.13 299.79,236.08 310.68,241.63C311.54,242.07 312.58,242.07 313.44,241.63C318.11,239.26 321.49,236.44 323.94,233.44C325.05,231.85 325.84,230.05 325.84,227.93C325.84,222.16 321.86,219.1 317.38,219.1Z"/>
+ <path android:fillColor="#7CACF8" android:pathData="M327.35,203.2L313.3,198.13C312.5,197.85 311.64,197.85 310.85,198.13L304.08,200.58C301.01,202.12 298.32,204.57 298.32,209.54C298.32,215.12 303.64,219.1 308.26,219.1H317.36C321.87,219.1 325.83,222.16 325.83,227.93C325.83,230.05 325.05,231.85 323.93,233.44C327.18,229.43 328.75,225.14 329.34,221.13C329.56,219.5 329.7,217.89 329.72,216.24V206.59C329.72,205.07 328.77,203.73 327.33,203.2H327.35Z"/>
+ <path android:fillColor="#ffffff" android:pathData="M333.81,202.84C333.81,200.94 332.63,199.26 330.85,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L292.96,198.6C291.18,199.26 290,200.94 290,202.84V214.9C290.06,216.96 290.19,218.94 290.47,221.01C291.76,229.76 296.68,239.7 310.18,246.63C311.25,247.18 312.54,247.18 313.6,246.63C327.1,239.67 332.03,229.76 333.32,221.01C333.59,218.97 333.76,216.96 333.78,214.9V202.84H333.81Z"/>
+ <path android:fillColor="#0842A0" android:pathData="M322.72,235.96C322.86,235.32 323.65,230.97 321.32,226.6C320.01,224.15 318.31,222.72 315.98,220.87C314.4,219.63 310.18,216.91 309.11,216.25C309.17,216.27 309.25,216.36 309.3,216.38C308.73,216.03 308.76,216.03 309.11,216.25C307.93,215.45 305.91,213.85 304.38,211.1C302.54,207.82 302.48,204.77 302.48,204.19C302.48,204.13 302.48,204.11 302.48,204.05C302.48,200.36 303.64,197.5 305.47,195.44C306.84,193.9 308.4,193.01 309.71,192.49L292.96,198.6C291.18,199.24 290,200.94 290,202.84V214.9C290.05,216.96 290.19,218.95 290.46,221.01C291.75,229.76 296.68,239.7 310.18,246.64C311.25,247.19 312.53,247.19 313.6,246.64C314.86,246 316.01,245.32 317.11,244.63C318.8,243.36 321.79,240.55 322.69,235.96H322.72Z"/>
+ <path android:fillColor="#ffffff" android:pathData="M309.31,216.38C309.31,216.38 309.17,216.27 309.12,216.24C308.76,216.02 308.73,216.02 309.31,216.38Z"/>
+ <path android:fillColor="#467CF1" android:pathData="M330.86,198.6L313.44,192.27C312.45,191.91 311.39,191.91 310.4,192.27L309.72,192.52C308.43,193.04 306.84,193.92 305.47,195.46C303.64,197.53 302.49,200.39 302.49,204.08C302.49,204.13 302.49,204.16 302.49,204.21C302.49,204.79 302.57,207.85 304.38,211.13C305.91,213.88 307.91,215.45 309.11,216.27C310.18,216.96 314.4,219.66 315.99,220.9C318.32,222.74 320.04,224.17 321.33,226.62C323.65,230.97 322.83,235.35 322.72,235.98C321.82,240.58 318.83,243.36 317.14,244.65C327.98,237.88 332.17,228.96 333.35,221.03C333.62,219 333.79,216.99 333.81,214.92V202.84C333.81,200.94 332.64,199.26 330.86,198.6Z"/>
+ <path android:fillColor="#1A73E8" android:pathData="M169.57,177V178H170.57H183.32H184.32V177V151.5V150.5H183.32H170.57H169.57V151.5V177ZM195.07,177V178H196.07H208.82H209.82V177V130.25V129.25H208.82H196.07H195.07V130.25V177ZM220.57,177V178H221.57H234.32H235.32V177V109V108H234.32H221.57H220.57V109V177Z" android:strokeColor="#1A73E8" android:strokeWidth="2"/>
+ <path android:fillColor="#E8EAED" android:pathData="M272.32,147.03V147.03C272.32,108.93 241.64,78 203.82,78C165.99,78 135.32,108.87 135.32,146.97C135.32,185.07 165.99,216 203.82,216C241.64,216 272.26,185.13 272.32,147.03ZM138.67,147.03C138.67,110.72 167.87,81.36 203.82,81.36C239.77,81.42 268.91,110.78 268.97,147.03C268.96,183.33 239.82,212.69 203.82,212.69C167.82,212.69 138.67,183.33 138.67,147.03Z" android:strokeColor="#E8EAED" android:strokeWidth="2"/>
+ <path android:fillColor="#1E8E3E" android:pathData="M225.17,207.55L225.17,207.55L225.16,207.56C223.69,208.09 223.33,209.76 223.79,211.04C224.03,211.7 224.44,212.25 225.1,212.5C225.73,212.73 226.35,212.61 226.84,212.44C255.99,201.88 271.55,177.91 272.99,148.23L272.99,148.23L272.99,148.22L273,148.06C273.02,147.36 272.8,146.68 272.42,146.15C272.05,145.62 271.48,145.17 270.78,145.02C269.22,144.7 268.07,145.97 268.02,147.37C267.07,172.62 252.22,198.21 225.17,207.55Z" android:strokeColor="#1E8E3E" android:strokeWidth="2"/>
+ <path android:fillColor="#1A73E8" android:pathData="M173.3,84.74L173.3,84.74L173.29,84.75C164.69,89.25 155.96,96.43 149.08,104.95C142.21,113.47 137.14,123.41 135.97,133.45L135.97,133.46C135.96,133.47 135.96,133.48 135.96,133.49C135.96,133.52 135.95,133.56 135.95,133.59L135.93,133.75L135.95,133.79C135.99,134.97 137.06,135.71 138.13,135.63C139.17,135.56 140.19,134.73 140.28,133.59C141.37,124.44 146.05,115.37 152.5,107.45C158.96,99.52 167.15,92.81 175.17,88.36C176.11,87.87 176.79,86.68 176.21,85.55C175.63,84.4 174.26,84.26 173.3,84.74Z" android:strokeColor="#1A73E8" android:strokeWidth="2"/>
+</vector>
diff --git a/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml
new file mode 100644
index 000000000..bb8d5b14d
--- /dev/null
+++ b/SafetyCenter/Resources/res/raw-v35/safety_center_config.xml
@@ -0,0 +1,152 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<safety-center-config>
+ <safety-sources-config>
+ <safety-sources-group
+ id="AndroidLockScreenSources"
+ title="@com.android.safetycenter.resources:string/lock_screen_sources_title"
+ summary="@com.android.safetycenter.resources:string/lock_screen_sources_summary">
+ <dynamic-safety-source
+ id="AndroidLockScreen"
+ packageName="com.android.settings"
+ profile="primary_profile_only"
+ title="@com.android.safetycenter.resources:string/lock_screen_title"
+ summary="@com.android.safetycenter.resources:string/lock_screen_summary_disabled"
+ searchTerms="@com.android.safetycenter.resources:string/lock_screen_search_terms"
+ initialDisplayState="disabled"
+ notificationsAllowed="true"/>
+ <dynamic-safety-source
+ id="AndroidBiometrics"
+ packageName="com.android.settings"
+ profile="all_profiles"
+ title="@com.android.safetycenter.resources:string/biometrics_title"
+ titleForWork="@com.android.safetycenter.resources:string/biometrics_title_for_work"
+ titleForPrivateProfile="@com.android.safetycenter.resources:string/biometrics_title_for_private_profile"
+ searchTerms="@com.android.safetycenter.resources:string/biometrics_search_terms"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidCellularNetworkSecuritySources"
+ title="@com.android.safetycenter.resources:string/cellular_network_security_title"
+ summary="@com.android.safetycenter.resources:string/cellular_network_security_summary">
+ <dynamic-safety-source
+ id="AndroidCellularNetworkSecurity"
+ packageName="com.android.phone"
+ profile="primary_profile_only"
+ notificationsAllowed="true"
+ initialDisplayState="hidden"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidPrivacySources"
+ title="@com.android.safetycenter.resources:string/privacy_sources_title"
+ summary="@com.android.safetycenter.resources:string/privacy_sources_summary"
+ statelessIconType="privacy">
+ <static-safety-source
+ id="AndroidPermissionManager"
+ profile="primary_profile_only"
+ intentAction="android.intent.action.MANAGE_PERMISSIONS"
+ title="@com.android.safetycenter.resources:string/permission_manager_title"
+ summary="@com.android.safetycenter.resources:string/permission_manager_summary"
+ searchTerms="@com.android.safetycenter.resources:string/permission_manager_search_terms"/>
+ <dynamic-safety-source
+ id="AndroidHealthConnect"
+ profile="primary_profile_only"
+ packageName="com.android.healthconnect.controller"
+ initialDisplayState="hidden"
+ refreshOnPageOpenAllowed="false"
+ title="@com.android.safetycenter.resources:string/health_connect_title"
+ searchTerms="@com.android.safetycenter.resources:string/health_connect_search_terms"/>
+ <dynamic-safety-source
+ id="AndroidPrivacyAppDataSharingUpdates"
+ packageName="com.android.permissioncontroller"
+ profile="primary_profile_only"
+ initialDisplayState="hidden"
+ refreshOnPageOpenAllowed="true"
+ title="@com.android.safetycenter.resources:string/app_data_sharing_updates_title"
+ searchTerms="@com.android.safetycenter.resources:string/app_data_sharing_updates_search_terms"/>
+ <static-safety-source
+ id="AndroidPrivacyControls"
+ profile="primary_profile_only"
+ intentAction="android.settings.PRIVACY_CONTROLS"
+ title="@com.android.safetycenter.resources:string/privacy_controls_title"
+ summary="@com.android.safetycenter.resources:string/privacy_controls_summary"
+ searchTerms="@com.android.safetycenter.resources:string/privacy_controls_search_terms"/>
+ <issue-only-safety-source
+ id="AndroidAccessibility"
+ packageName="com.android.permissioncontroller"
+ profile="all_profiles"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidNotificationListener"
+ packageName="com.android.permissioncontroller"
+ profile="primary_profile_only"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidBackgroundLocation"
+ packageName="com.android.permissioncontroller"
+ profile="all_profiles"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ <issue-only-safety-source
+ id="AndroidPermissionAutoRevoke"
+ packageName="com.android.permissioncontroller"
+ profile="all_profiles"
+ notificationsAllowed="true"
+ refreshOnPageOpenAllowed="true"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidPrivacySourcesAdditional"
+ title="@com.android.safetycenter.resources:string/privacy_additional_title">
+ <static-safety-source
+ id="AndroidPermissionUsage"
+ profile="primary_profile_only"
+ intentAction="android.intent.action.REVIEW_PERMISSION_USAGE"
+ title="@com.android.safetycenter.resources:string/permission_usage_title"
+ summary="@com.android.safetycenter.resources:string/permission_usage_summary"
+ searchTerms="@com.android.safetycenter.resources:string/permission_usage_search_terms"/>
+ <dynamic-safety-source
+ id="AndroidPrivateSpace"
+ packageName="com.android.settings"
+ profile="primary_profile_only"
+ title="@com.android.safetycenter.resources:string/private_space_title"
+ summary="@com.android.safetycenter.resources:string/private_space_summary"
+ searchTerms="@com.android.safetycenter.resources:string/private_space_search_terms"
+ initialDisplayState="hidden"
+ maxSeverityLevel="0"/>
+ </safety-sources-group>
+ <safety-sources-group
+ id="AndroidAdvancedSources"
+ title="@com.android.safetycenter.resources:string/advanced_title">
+ <dynamic-safety-source
+ id="AndroidWorkPolicyInfo"
+ packageName="com.android.permissioncontroller"
+ profile="primary_profile_only"
+ title="@com.android.safetycenter.resources:string/work_policy_title"
+ initialDisplayState="hidden"
+ refreshOnPageOpenAllowed="true"/>
+ <static-safety-source
+ id="AndroidMoreSettings"
+ profile="primary_profile_only"
+ intentAction="com.android.settings.MORE_SECURITY_PRIVACY_SETTINGS"
+ title="@com.android.safetycenter.resources:string/more_settings_title"
+ summary="@com.android.safetycenter.resources:string/more_settings_summary"
+ searchTerms="@com.android.safetycenter.resources:string/more_settings_search_terms"/>
+ </safety-sources-group>
+ </safety-sources-config>
+</safety-center-config>
diff --git a/SafetyCenter/Resources/res/values-af-v35/strings.xml b/SafetyCenter/Resources/res/values-af-v35/strings.xml
new file mode 100644
index 000000000..db0f9ab8b
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-af-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Selnetwerksekuriteit"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Netwerktipe, enkripsie, kennisgewingkontroles"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privaatheid"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privaatheidkontroles"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Toestemmings, kontroles"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privaat ruimte"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Stel privaat ruimte op, en meer"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privaat ruimte"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-am-v35/strings.xml b/SafetyCenter/Resources/res/values-am-v35/strings.xml
new file mode 100644
index 000000000..d4aae2330
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-am-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"የተንቀሳቃሽ ስልክ አውታረ መረብ ደህንነት"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"የአውታረ መረብ ዓይነት፣ ምስጠራ፣ የማሳወቂያ መቆጣጠሪያዎች"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ግላዊነት"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"የግላዊነት መቆጣጠሪያዎች"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ፈቃዶች፣ መቆጣጠሪያዎች"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"የግል ቦታ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"የግል ቦታን እና ሌሎችን ያዋቅሩ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"የግል ቦታ"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ar-v35/strings.xml b/SafetyCenter/Resources/res/values-ar-v35/strings.xml
new file mode 100644
index 000000000..639815c89
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ar-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"أمان شبكة الجوّال"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"عناصر التحكم في نوع الشبكة والتشفير والإشعارات"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"الخصوصية"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"عناصر التحكّم في الخصوصية"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"الأذونات وعناصر التحكّم"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"مساحة خاصة"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ضبط إعدادات مساحة خاصة وغير ذلك"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"مساحة خاصة"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-as-v35/strings.xml b/SafetyCenter/Resources/res/values-as-v35/strings.xml
new file mode 100644
index 000000000..e3afe4c9e
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-as-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"চেলুলাৰ নেটৱৰ্কৰ সুৰক্ষা"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"নেটৱৰ্কৰ প্ৰকাৰ, এনক্ৰিপশ্বন, জাননীৰ নিয়ন্ত্ৰণসমূহ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"গোপনীয়তা"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"গোপনীয়তাৰ নিয়ন্ত্ৰণ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"অনুমতিসমূহ, নিয়ন্ত্ৰণসমূহ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"প্ৰাইভেট স্পে\'চ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"প্ৰাইভেট স্পে\'চ আৰু আন বহুতো ছেটআপ কৰক"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"প্ৰাইভেট স্পে\'চ"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-az-v35/strings.xml b/SafetyCenter/Resources/res/values-az-v35/strings.xml
new file mode 100644
index 000000000..64b414421
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-az-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobil şəbəkə təhlükəsizliyi"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Şəbəkə növü, şifrələmə, bildiriş nizamlayıcıları"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Məxfilik"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Məxfilik nizamlayıcıları"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"İcazələr, nizamlayıcılar"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Şəxsi yer"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Şəxsi yer və s. ayarlayın"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Şəxsi yer"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-b+sr+Latn-v35/strings.xml b/SafetyCenter/Resources/res/values-b+sr+Latn-v35/strings.xml
new file mode 100644
index 000000000..af536c383
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-b+sr+Latn-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Bezbednost mobilne mreže"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tip mreže, šifrovanje, kontrole obaveštenja"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privatnost"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Kontrole privatnosti"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Dozvole, kontrole"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privatni prostor"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Podesite privatni prostor i drugo"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privatni prostor"</string>
+</resources>
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-be-v35/strings.xml b/SafetyCenter/Resources/res/values-be-v35/strings.xml
new file mode 100644
index 000000000..55daf6e66
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-be-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Бяспека сотавай сеткі"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тып сеткі, шыфраванне, налады апавяшчэнняў"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Прыватнасць"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Налады прыватнасці"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Дазволы, налады"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Прыватная вобласць"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Наладжванне прыватнай вобласці і не толькі"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Прыватная вобласць"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-bg-v35/strings.xml b/SafetyCenter/Resources/res/values-bg-v35/strings.xml
new file mode 100644
index 000000000..99d85239a
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-bg-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Сигурност на мобилната мрежа"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тип мрежа, шифроване, контроли за известията"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Поверителност"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Контроли за поверителност"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Разрешения, контроли"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Лично пространство"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Настройване на лично пространство и др."</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Лично пространство"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-bn-v35/strings.xml b/SafetyCenter/Resources/res/values-bn-v35/strings.xml
new file mode 100644
index 000000000..644f8fa18
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-bn-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"মোবাইল নেটওয়ার্কের সুরক্ষা"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"নেটওয়ার্কের ধরন, এনক্রিপশন, বিজ্ঞপ্তির নিয়ন্ত্রণ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"গোপনীয়তা"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"গোপনীয়তা নিয়ন্ত্রণ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"অনুমতি, কন্ট্রোল"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ব্যক্তিগত স্পেস"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"\'ব্যক্তিগত স্পেস\' সেট-আপ ও আরও অনেক কিছু করুন"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ব্যক্তিগত স্পেস"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-bs-v35/strings.xml b/SafetyCenter/Resources/res/values-bs-v35/strings.xml
new file mode 100644
index 000000000..cce4ac279
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-bs-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sigurnost mobilne mreže"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Vrsta mreže, šifriranje i kontrole obavještenja"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privatnost"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Kontrole privatnosti"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Odobrenja, kontrole"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privatni prostor"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Postavite privatni prostor i drugo"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privatni prostor"</string>
+</resources>
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-ca-v35/strings.xml b/SafetyCenter/Resources/res/values-ca-v35/strings.xml
new file mode 100644
index 000000000..20fe7e6ad
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ca-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Seguretat de la xarxa mòbil"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipus de xarxa, encriptació, controls de notificació"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privadesa"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controls de privadesa"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permisos, controls"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espai privat"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configura l\'espai privat i més"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espai privat"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-cs-v35/strings.xml b/SafetyCenter/Resources/res/values-cs-v35/strings.xml
new file mode 100644
index 000000000..d40f1ae64
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-cs-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Zabezpečení mobilní sítě"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Typ sítě, šifrování, ovládání oznámení"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Ochrana soukromí"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Nastavení ochrany soukromí"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Oprávnění, ovládací prvky"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Soukromý prostor"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Nastavte si Soukromý prostor atd."</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Soukromý prostor"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-da-v35/strings.xml b/SafetyCenter/Resources/res/values-da-v35/strings.xml
new file mode 100644
index 000000000..e57c93033
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-da-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sikkerhed for mobilnetværk"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Netværkstype, kryptering, notifikationsstyring"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privatliv"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privatlivsindstillinger"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Tilladelser, indstillinger"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privat område"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Konfigurer et privat rum m.m."</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privat område"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-de-v35/strings.xml b/SafetyCenter/Resources/res/values-de-v35/strings.xml
new file mode 100644
index 000000000..6c43218d2
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-de-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sicherheit von Mobilfunknetzen"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Netzwerktyp, Verschlüsselung, Benachrichtigungseinstellungen"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Datenschutz"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Datenschutzeinstellungen"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Berechtigungen, Einstellungen"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Vertrauliches Profil"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Vertrauliches Profil einrichten und mehr"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Vertrauliches Profil"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-el-v35/strings.xml b/SafetyCenter/Resources/res/values-el-v35/strings.xml
new file mode 100644
index 000000000..7e83565d2
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-el-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Ασφάλεια δικτύου κινητής τηλεφωνίας"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Τύπος δικτύου, κρυπτογράφηση, στοιχεία ελέγχου ειδοποιήσεων"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Απόρρητο"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Στοιχεία ελέγχου απορρήτου"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Άδειες, στοιχεία ελέγχου"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Ιδιωτικός χώρος"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Ρύθμιση Ιδιωτικού χώρου κ.ά."</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Ιδιωτικός χώρος"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-en-rAU-v35/strings.xml b/SafetyCenter/Resources/res/values-en-rAU-v35/strings.xml
new file mode 100644
index 000000000..7fc38c945
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-en-rAU-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobile network security"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Network type, encryption, notification controls"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privacy controls"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permissions, controls"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Private Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Set up Private Space, and more"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Private Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-en-rCA-v35/strings.xml b/SafetyCenter/Resources/res/values-en-rCA-v35/strings.xml
new file mode 100644
index 000000000..a07241022
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-en-rCA-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Cellular network security"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Network type, encryption, notification controls"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privacy controls"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permissions, controls"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Private Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Setup Private Space, and more"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Private Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-en-rGB-v35/strings.xml b/SafetyCenter/Resources/res/values-en-rGB-v35/strings.xml
new file mode 100644
index 000000000..7fc38c945
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-en-rGB-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobile network security"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Network type, encryption, notification controls"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privacy controls"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permissions, controls"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Private Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Set up Private Space, and more"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Private Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-en-rIN-v35/strings.xml b/SafetyCenter/Resources/res/values-en-rIN-v35/strings.xml
new file mode 100644
index 000000000..7fc38c945
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-en-rIN-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobile network security"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Network type, encryption, notification controls"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privacy controls"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permissions, controls"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Private Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Set up Private Space, and more"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Private Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-en-rXC-v35/strings.xml b/SafetyCenter/Resources/res/values-en-rXC-v35/strings.xml
new file mode 100644
index 000000000..c92958669
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-en-rXC-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‏‏‏‎‏‎Cellular network security‎‏‎‎‏‎"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎Network type, encryption, notification controls‎‏‎‎‏‎"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎Privacy‎‏‎‎‏‎"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‎Privacy controls‎‏‎‎‏‎"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎Permissions, controls‎‏‎‎‏‎"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‎Private Space‎‏‎‎‏‎"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎Setup Private Space, and more‎‏‎‎‏‎"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‎‎Private Space‎‏‎‎‏‎"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-es-rUS-v35/strings.xml b/SafetyCenter/Resources/res/values-es-rUS-v35/strings.xml
new file mode 100644
index 000000000..9b17255b5
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-es-rUS-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Seguridad de red móvil"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo de red, encriptación, controles de notificaciones"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacidad"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controles de privacidad"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permisos y controles"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espacio privado"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configura el Espacio privado y mucho más"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espacio privado"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-es-v35/strings.xml b/SafetyCenter/Resources/res/values-es-v35/strings.xml
new file mode 100644
index 000000000..74ea5fa7f
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-es-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Seguridad de la red móvil"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo de red, cifrado, controles de notificaciones"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacidad"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controles de privacidad"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permisos, controles"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espacio privado"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configura el espacio privado y más"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espacio privado"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-et-v35/strings.xml b/SafetyCenter/Resources/res/values-et-v35/strings.xml
new file mode 100644
index 000000000..36ca50ec4
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-et-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobiilsidevõrgu turvalisus"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Võrgu tüüp, krüpteerimine, märguannete juhtnupud"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privaatsus"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privaatsuse seaded"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Load, juhtelemendid"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privaatne ruum"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Privaatse ruumi seadistamine ja muu"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privaatne ruum"</string>
+</resources>
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-eu-v35/strings.xml b/SafetyCenter/Resources/res/values-eu-v35/strings.xml
new file mode 100644
index 000000000..bd23775ee
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-eu-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sare mugikorraren segurtasuna"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Sare mota, enkriptatzea, jakinarazpenak kontrolatzeko aukerak"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Pribatutasuna"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Pribatutasun-ezarpenak"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Baimenak, kontrolatzeko aukerak"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Eremu pribatua"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Konfiguratu eremu pribatua eta abar"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Eremu pribatua"</string>
+</resources>
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-v35/strings.xml b/SafetyCenter/Resources/res/values-fa-v35/strings.xml
new file mode 100644
index 000000000..878c10bb4
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-fa-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"امنیت شبکه تلفن همراه"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"نوع شبکه، رمزگذاری، کنترل‌های اعلان"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"حریم خصوصی"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"تنظیمات حریم خصوصی"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"اجازه‌ها، کنترل‌ها"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"فضای خصوصی"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"راه‌اندازی «فضای خصوصی»، و موارد دیگر"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"فضای خصوصی"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-fa/strings.xml b/SafetyCenter/Resources/res/values-fa/strings.xml
index cf277b7c1..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>
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-v35/strings.xml b/SafetyCenter/Resources/res/values-fi-v35/strings.xml
new file mode 100644
index 000000000..aef9a528e
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-fi-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Puhelinverkon turvallisuus"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Verkon tyyppi, salaus, ilmoitusvalinnat"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Yksityisyys"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Yksityisyysasetukset"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Luvat, asetukset"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Yksityinen tila"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Ota esimerkiksi yksityinen tila käyttöön"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Yksityinen tila"</string>
+</resources>
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-v35/strings.xml b/SafetyCenter/Resources/res/values-fr-rCA-v35/strings.xml
new file mode 100644
index 000000000..a27b04ad9
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-fr-rCA-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sécurité du réseau cellulaire"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Contrôles du type de réseau, du chiffrement et des notifications"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Confidentialité"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Paramètres de confidentialité"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Autorisations, commandes"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espace privé"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configuration de l\'espace privé, et plus"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espace privé"</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-v35/strings.xml b/SafetyCenter/Resources/res/values-fr-v35/strings.xml
new file mode 100644
index 000000000..b9af6233c
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-fr-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sécurité des réseaux mobiles"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Type de réseau, chiffrement, paramètres de notifications"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Confidentialité"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Paramètres de confidentialité"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Autorisations et commandes"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espace privé"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configurez votre espace privé et bien plus"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espace privé"</string>
+</resources>
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-gl-v35/strings.xml b/SafetyCenter/Resources/res/values-gl-v35/strings.xml
new file mode 100644
index 000000000..4064cbb58
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-gl-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Seguranza da rede de telefonía móbil"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo de rede, encriptación, controis de notificacións"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacidade"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controis de privacidade"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permisos, controis"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espazo privado"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configura o espazo privado e moito máis"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espazo privado"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-gu-v35/strings.xml b/SafetyCenter/Resources/res/values-gu-v35/strings.xml
new file mode 100644
index 000000000..09246930e
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-gu-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"સેલ્યુલર નેટવર્ક સંબંધી સુરક્ષા"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"નેટવર્કનો પ્રકાર, એન્ક્રિપ્શન, નોટિફિકેશનના નિયંત્રણો"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"પ્રાઇવસી"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"પ્રાઇવસીને લગતા નિયંત્રણો"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"પરવાનગીઓ, નિયંત્રણો"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ખાનગી સ્પેસ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ખાનગી સ્પેસનું સેટઅપ કરો અને બીજું ઘણું કરો"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ખાનગી સ્પેસ"</string>
+</resources>
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-hi-v35/strings.xml b/SafetyCenter/Resources/res/values-hi-v35/strings.xml
new file mode 100644
index 000000000..bf822f271
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-hi-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"मोबाइल नेटवर्क की सुरक्षा"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"नेटवर्क टाइप, एन्क्रिप्ट करने का तरीका, सूचनाएं कंट्रोल करने की सेटिंग"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"निजता"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"निजता सेटिंग"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"अनुमतियां, कंट्रोल"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"प्राइवेट स्पेस"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"प्राइवेट स्पेस सेटअप करें और अन्य काम करें"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"प्राइवेट स्पेस"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-hr-v35/strings.xml b/SafetyCenter/Resources/res/values-hr-v35/strings.xml
new file mode 100644
index 000000000..1609a1fc2
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-hr-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sigurnost mobilne mreže"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Vrsta mreže, šifriranje, kontrole obavijesti"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privatnost"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Kontrole privatnosti"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Dopuštenja, kontrole"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privatni prostor"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Postavljanje privatnog prostora i drugo"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privatni prostor"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-hu-v35/strings.xml b/SafetyCenter/Resources/res/values-hu-v35/strings.xml
new file mode 100644
index 000000000..b4b92c81b
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-hu-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobilhálózat biztonsága"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Hálózattípus, titkosítás, értesítésvezérlők"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Adatvédelem"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Adatvédelmi beállítások"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Engedélyek, vezérlők"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privát terület"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Privát terület beállítása és egyebek"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privát terület"</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-hy-v35/strings.xml b/SafetyCenter/Resources/res/values-hy-v35/strings.xml
new file mode 100644
index 000000000..dd5c9e01b
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-hy-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Բջջային ցանցի անվտանգություն"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Ցանցի տեսակը, գաղտնագրում, ծանուցումների կառավարում"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Գաղտնիություն"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Գաղտնիության կարգավորումներ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Թույլտվություններ, կարգավորումներ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Անձնական տարածք"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Կարգավորեք մասնավոր տարածքը և ավելին"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Անձնական տարածք"</string>
+</resources>
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-in-v35/strings.xml b/SafetyCenter/Resources/res/values-in-v35/strings.xml
new file mode 100644
index 000000000..fdbb8d8f9
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-in-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Keamanan jaringan seluler"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Jenis jaringan, enkripsi, kontrol notifikasi"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privasi"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Kontrol privasi"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Izin, kontrol"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Ruang Privasi"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Menyiapkan Ruang Pribadi, dan lainnya"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Ruang Privasi"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-is-v35/strings.xml b/SafetyCenter/Resources/res/values-is-v35/strings.xml
new file mode 100644
index 000000000..2e86bc943
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-is-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Öryggi farsímakerfis"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tegund netkerfis, dulkóðun, tilkynningastýringar"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Persónuvernd"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Persónuverndarstillingar"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Heimildir, stýringar"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Leynirými"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Setja upp leynirými og fleira"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Leynirými"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-it-v35/strings.xml b/SafetyCenter/Resources/res/values-it-v35/strings.xml
new file mode 100644
index 000000000..693118f31
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-it-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sicurezza rete mobile"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo di rete, crittografia, controlli di notifica"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controlli per la privacy"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Autorizzazioni, controlli"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Spazio privato"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configura lo Spazio privato e altro ancora"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Spazio privato"</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-iw-v35/strings.xml b/SafetyCenter/Resources/res/values-iw-v35/strings.xml
new file mode 100644
index 000000000..d3c8e9905
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-iw-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"אבטחת הרשת הסלולרית"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"סוג הרשת, הצפנה, אמצעי בקרה של התראות"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"פרטיות"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"אמצעי בקרה על פרטיות"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"הרשאות, אמצעי בקרה"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"מרחב פרטי"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"הגדרת מרחב פרטי ועוד"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"מרחב פרטי"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ja-v35/strings.xml b/SafetyCenter/Resources/res/values-ja-v35/strings.xml
new file mode 100644
index 000000000..c95db2cac
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ja-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"モバイル ネットワークのセキュリティ"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ネットワークの種類、暗号化、通知の管理"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"プライバシー"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"プライバシー管理"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"権限、管理"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"プライベート スペース"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"プライベート スペースの設定、その他"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"プライベート スペース"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ka-v35/strings.xml b/SafetyCenter/Resources/res/values-ka-v35/strings.xml
new file mode 100644
index 000000000..d5a444deb
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ka-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"ფიჭური ქსელის უსაფრთხოება"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ქსელის ტიპი, დაშიფვრა, შეტყობინებების მართვის საშუალებები"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"კონფიდენციალურობა"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"კონფიდენციალურობის მართვის პარამეტრები"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ნებართვები, მართვის საშუალებები"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"პირადი სივრცე"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"დააყენეთ პირადი სივრცე და ა.შ."</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"პირადი სივრცე"</string>
+</resources>
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-kk-v35/strings.xml b/SafetyCenter/Resources/res/values-kk-v35/strings.xml
new file mode 100644
index 000000000..0956a1fce
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-kk-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Ұялы желі қауіпсіздігі"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Желі түрі, шифрлауды, хабарландыруды басқару элементтері"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Құпиялық"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Құпиялық параметрлері"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Рұқсаттар, басқару элементтері"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Құпия кеңістік"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Жеке бөлмені реттеу және т.б."</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Құпия кеңістік"</string>
+</resources>
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-km-v35/strings.xml b/SafetyCenter/Resources/res/values-km-v35/strings.xml
new file mode 100644
index 000000000..35112e348
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-km-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"សុវត្ថិភាពបណ្ដាញចល័ត"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ប្រភេទបណ្ដាញ ការអ៊ីនគ្រីប ការគ្រប់គ្រងការជូនដំណឹង"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ឯកជនភាព"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"ការគ្រប់គ្រង​ឯកជនភាព"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ការអនុញ្ញាត ការគ្រប់គ្រង"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"បន្ទប់​ឯកជន"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"រៀបចំបន្ទប់ឯកជន និងធ្វើអ្វីៗជាច្រើនទៀត"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"បន្ទប់​ឯកជន"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-kn-v35/strings.xml b/SafetyCenter/Resources/res/values-kn-v35/strings.xml
new file mode 100644
index 000000000..339e522d9
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-kn-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"ಸೆಲ್ಯುಲಾರ್ ನೆಟ್‌ವರ್ಕ್ ಭದ್ರತೆ"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ನೆಟ್‌ವರ್ಕ್ ಪ್ರಕಾರ, ಎನ್‌ಕ್ರಿಪ್ಶನ್, ನೋಟಿಫಿಕೇಶನ್ ಕಂಟ್ರೋಲ್‌ಗಳು"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ಗೌಪ್ಯತೆ"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"ಗೌಪ್ಯತೆ ನಿಯಂತ್ರಣಗಳು"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ಅನುಮತಿಗಳು, ನಿಯಂತ್ರಣಗಳು"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ ಹಾಗೂ ಇನ್ನಷ್ಟನ್ನು ಮಾಡಿ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ಖಾಸಗಿ ಸ್ಪೇಸ್"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ko-v35/strings.xml b/SafetyCenter/Resources/res/values-ko-v35/strings.xml
new file mode 100644
index 000000000..af2d8bcc6
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ko-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"셀룰러 네트워크 보안"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"네트워크 유형, 암호화, 알림 설정"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"개인 정보 보호"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"개인 정보 보호 설정"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"권한, 제어"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"비공개 스페이스"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"비공개 스페이스 설정 등"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"비공개 스페이스"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ky-v35/strings.xml b/SafetyCenter/Resources/res/values-ky-v35/strings.xml
new file mode 100644
index 000000000..201099148
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ky-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Мобилдик тармактын коопсуздугу"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тармактын түрү, шифрлөө, билдирмелерди башкаруу элементтери"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Купуялык"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Купуялыкты көзөмөлдөө каражаттары"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Уруксаттар, башкаруу элементтери"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Жеке мейкиндик"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Жеке мейкиндикти тууралоо жана башка нерселер"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Жеке мейкиндик"</string>
+</resources>
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-lo-v35/strings.xml b/SafetyCenter/Resources/res/values-lo-v35/strings.xml
new file mode 100644
index 000000000..606339144
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-lo-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"ຄວາມປອດໄພຂອງເຄືອຂ່າຍມືຖື"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ປະເພດເຄືອຂ່າຍ, ການເຂົ້າລະຫັດ, ການຄວບຄຸມການແຈ້ງເຕືອນ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ຄວາມເປັນສ່ວນຕົວ"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"ການຄວບຄຸມຄວາມເປັນສ່ວນຕົວ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ການອະນຸຍາດ, ການຄວບຄຸມ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ພື້ນທີ່ສ່ວນຕົວ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ຕັ້ງຄ່າພື້ນທີ່ສ່ວນຕົວ ແລະ ອື່ນໆ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ພື້ນທີ່ສ່ວນຕົວ"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-lt-v35/strings.xml b/SafetyCenter/Resources/res/values-lt-v35/strings.xml
new file mode 100644
index 000000000..03360bfd5
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-lt-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobiliojo ryšio tinklo sauga"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tinklo tipas, šifruotė, pranešimų valdikliai"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privatumas"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privatumo valdikliai"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Leidimai, valdikliai"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privati erdvė"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Nustatykite privačią erdvę ir atlikite kitų veiksmų"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privati erdvė"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-lv-v35/strings.xml b/SafetyCenter/Resources/res/values-lv-v35/strings.xml
new file mode 100644
index 000000000..f26fcccc3
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-lv-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobilā tīkla drošība"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tīkla veids, šifrējums, paziņojumu vadīklas"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Konfidencialitāte"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Konfidencialitātes vadīklas"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Atļaujas, vadīklas"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privātā mape"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Privātās mapes iestatīšana un citas iespējas"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privātā mape"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-mk-v35/strings.xml b/SafetyCenter/Resources/res/values-mk-v35/strings.xml
new file mode 100644
index 000000000..c4fc4bd17
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-mk-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Безбедност на мобилната мрежа"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тип мрежа, шифрирање, контроли за известувања"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Приватност"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Контроли на приватноста"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Дозволи, контроли"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Приватен простор"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Поставување „Приватен простор“ и друго"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Приватен простор"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ml-v35/strings.xml b/SafetyCenter/Resources/res/values-ml-v35/strings.xml
new file mode 100644
index 000000000..a91c131a2
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ml-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"സെല്ലുലാർ നെറ്റ്‌വർക്ക് സുരക്ഷ"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"നെറ്റ്‌വർക്ക് തരം, എൻ‍ക്രിപ്ഷൻ, അറിയിപ്പ് നിയന്ത്രണങ്ങൾ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"സ്വകാര്യത"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"സ്വകാര്യതാ നിയന്ത്രണങ്ങൾ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"അനുമതികൾ, നിയന്ത്രണങ്ങൾ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"സ്വകാര്യ Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"സ്വകാര്യ Space സജ്ജീകരിക്കുകയും മറ്റും ചെയ്യൂ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"സ്വകാര്യ Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-mn-v35/strings.xml b/SafetyCenter/Resources/res/values-mn-v35/strings.xml
new file mode 100644
index 000000000..20ef5b3ad
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-mn-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Үүрэн холбооны сүлжээний аюулгүй байдал"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Сүлжээний төрөл, шифрлэлт, мэдэгдлийн тохиргоо"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Нууцлал"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Нууцлалын тохиргоо"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Зөвшөөрөл, тохиргоо"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Хувийн орон зай"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Хувийн орон зай тохируулах болон илүү ихийг хийх"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Хувийн орон зай"</string>
+</resources>
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-mr-v35/strings.xml b/SafetyCenter/Resources/res/values-mr-v35/strings.xml
new file mode 100644
index 000000000..c3c1c1df1
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-mr-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"मोबाइल नेटवर्कची सुरक्षा"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"नेटवर्क प्रकार, एन्क्रिप्शन, सूचना नियंत्रणे"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"गोपनीयता"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"गोपनीयता नियंत्रणे"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"परवानग्या, नियंत्रणे"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"खाजगी स्पेस"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"खाजगी स्पेस आणि आणखी बरेच काही सेट करा"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"खाजगी स्पेस"</string>
+</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-ms-v35/strings.xml b/SafetyCenter/Resources/res/values-ms-v35/strings.xml
new file mode 100644
index 000000000..202f7b4e2
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ms-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Keselamatan rangkaian selular"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Jenis rangkaian, penyulitan, kawalan pemberitahuan"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privasi"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Kawalan privasi"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Kebenaran, kawalan"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Ruang Peribadi"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Sediakan Ruang Peribadi dan pelbagai lagi"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Ruang Peribadi"</string>
+</resources>
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-v35/strings.xml b/SafetyCenter/Resources/res/values-my-v35/strings.xml
new file mode 100644
index 000000000..7478848dd
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-my-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"ဆယ်လူလာကွန်ရက် လုံခြုံရေး"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ကွန်ရက်အမျိုးအစား၊ အသွင်ဝှက်ခြင်း၊ အကြောင်းကြားချက် သတ်မှတ်ချက်များ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"ကိုယ်ရေးအချက်အလက်လုံခြုံမှု ဆက်တင်များ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ခွင့်ပြုချက်များ၊ သတ်မှတ်ချက်များ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"သီးသန့်နေရာ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"သီးသန့်ချတ်ခန်း စသည်တို့ကို စနစ်ထည့်သွင်းသည်"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"သီးသန့်နေရာ"</string>
+</resources>
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-nb-v35/strings.xml b/SafetyCenter/Resources/res/values-nb-v35/strings.xml
new file mode 100644
index 000000000..2c38c6671
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-nb-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Sikkerhet for mobilnettverk"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Nettverkstype, kryptering, varselskontroller"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Personvern"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Personverninnstillinger"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Tillatelser, kontroller"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Private Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Konfigurer Private Space med mer"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Private Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ne-v35/strings.xml b/SafetyCenter/Resources/res/values-ne-v35/strings.xml
new file mode 100644
index 000000000..23b92803e
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ne-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"मोबाइल नेटवर्कको सुरक्षा"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"नेटवर्कको प्रकार, इन्क्रिप्सन, सूचनाका सेटिङ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"गोपनीयता"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"गोपनीयतासम्बन्धी सेटिङ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"अनुमति, सेटिङ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"निजी स्पेस"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"निजी स्पेस सेटअप गर्नुहोस् र अन्य कार्यहरू गर्नुहोस्"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"निजी स्पेस"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-nl-v35/strings.xml b/SafetyCenter/Resources/res/values-nl-v35/strings.xml
new file mode 100644
index 000000000..c0fa2f4c5
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-nl-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Beveiliging van mobiele netwerken"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Netwerktype, versleuteling, beheeropties voor meldingen"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Privacyopties"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Rechten, beheeropties"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privéruimte"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Privéruimte instellen en meer"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privéruimte"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-or-v35/strings.xml b/SafetyCenter/Resources/res/values-or-v35/strings.xml
new file mode 100644
index 000000000..5023efd4d
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-or-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"ସେଲୁଲାର ନେଟୱାର୍କ ସୁରକ୍ଷା"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ନେଟୱାର୍କ ପ୍ରକାର, ଏନକ୍ରିପସନ, ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ଗୋପନୀୟତା"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"ଗୋପନୀୟତା ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ଅନୁମତି, ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ପ୍ରାଇଭେଟ ସ୍ପେସ ଏବଂ ଆହୁରି ଅନେକ କିଛି ସେଟଅପ କରନ୍ତୁ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
+</resources>
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-pa-v35/strings.xml b/SafetyCenter/Resources/res/values-pa-v35/strings.xml
new file mode 100644
index 000000000..2196a55d1
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-pa-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"ਸੈਲਿਊਲਰ ਨੈੱਟਵਰਕ ਸੁਰੱਖਿਆ"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ਨੈੱਟਵਰਕ ਦੀ ਕਿਸਮ, ਇਨਕ੍ਰਿਪਸ਼ਨ, ਸੂਚਨਾ ਕੰਟਰੋਲ"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ਪਰਦੇਦਾਰੀ"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"ਪਰਦੇਦਾਰੀ ਕੰਟਰੋਲ"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"ਇਜਾਜ਼ਤਾਂ, ਕੰਟਰੋਲ"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਦਾ ਸੈੱਟਅੱਪ ਅਤੇ ਹੋਰ ਕੰਮ ਕਰੋ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-pl-v35/strings.xml b/SafetyCenter/Resources/res/values-pl-v35/strings.xml
new file mode 100644
index 000000000..0d982ff7e
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-pl-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Zabezpieczenia sieci komórkowej"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Typ sieci, szyfrowanie, ustawienia powiadomień"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Prywatność"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Ustawienia prywatności"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Uprawnienia, opcje"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Przestrzeń prywatna"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Skonfiguruj obszar prywatny i inne ustawienia"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Przestrzeń prywatna"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-pt-rBR-v35/strings.xml b/SafetyCenter/Resources/res/values-pt-rBR-v35/strings.xml
new file mode 100644
index 000000000..705ceb624
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-pt-rBR-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Segurança da rede celular"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo de rede, criptografia, controles de notificação"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacidade"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controles de privacidade"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permissões, controles"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espaço privado"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configurar Espaço particular e mais"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espaço privado"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-pt-rPT-v35/strings.xml b/SafetyCenter/Resources/res/values-pt-rPT-v35/strings.xml
new file mode 100644
index 000000000..8666b15b2
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-pt-rPT-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Segurança da rede móvel"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo de rede, encriptação, controlos de notificação"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacidade"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controlos de privacidade"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Autorizações, controlos"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espaço privado"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configure o espaço privado e muito mais"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espaço privado"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-pt-v35/strings.xml b/SafetyCenter/Resources/res/values-pt-v35/strings.xml
new file mode 100644
index 000000000..705ceb624
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-pt-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Segurança da rede celular"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipo de rede, criptografia, controles de notificação"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacidade"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Controles de privacidade"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permissões, controles"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Espaço privado"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configurar Espaço particular e mais"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Espaço privado"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ro-v35/strings.xml b/SafetyCenter/Resources/res/values-ro-v35/strings.xml
new file mode 100644
index 000000000..8b8e8bbfa
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ro-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Securitatea rețelei de date mobile"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tipul de rețea, criptarea, comenzile pentru notificări"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Confidențialitate"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Opțiuni de confidențialitate"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Permisiuni, comenzi"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Spațiu privat"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Configurează Spațiul privat și altele"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Spațiu privat"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ru-v34/strings.xml b/SafetyCenter/Resources/res/values-ru-v34/strings.xml
index b8a78d5ed..70b139c91 100644
--- a/SafetyCenter/Resources/res/values-ru-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-ru-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-v35/strings.xml b/SafetyCenter/Resources/res/values-ru-v35/strings.xml
new file mode 100644
index 000000000..87f9e0572
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ru-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Безопасность мобильной сети"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тип сети, шифрование, управление уведомлениями"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Конфиденциальность"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Настройки конфиденциальности"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Разрешения и параметры"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Частное пространство"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Настройка личного пространства и не только"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Частное пространство"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-si-v35/strings.xml b/SafetyCenter/Resources/res/values-si-v35/strings.xml
new file mode 100644
index 000000000..bcff52b2a
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-si-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"සෙලියුලර් ජාල ආරක්ෂාව"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ජාල වර්ගය, සංකේතනය, දැනුම්දීම් පාලන"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"පෞද්ගලිකත්වය"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"පෞද්ගලිකත්ව පාලන"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"අවසර, පාලන"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"පෞද්ගලික ඉඩ"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"පෞද්ගලික ඉඩ, සහ තවත් දේ පිහිටුවන්න"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"පෞද්ගලික ඉඩ"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-sk-v35/strings.xml b/SafetyCenter/Resources/res/values-sk-v35/strings.xml
new file mode 100644
index 000000000..e95c443a6
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-sk-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Zabezpečenie mobilnej siete"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Ovládanie typu siete, šifrovania a upozornení"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Ochrana súkromia"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Nastavenia ochrany súkromia"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Povolenia, ovládanie"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Súkromný priestor"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Nastavte súkromný priestor a ďalšie možnosti"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Súkromný priestor"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-sl-v35/strings.xml b/SafetyCenter/Resources/res/values-sl-v35/strings.xml
new file mode 100644
index 000000000..588015c3a
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-sl-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Varnost mobilnega omrežja"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Vrsta omrežja, šifriranje, kontrolniki obvestil"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Zasebnost"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Nastavitve zasebnosti"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Dovoljenja, nastavitve"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Zasebni prostor"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Nastavitev zasebnega prostora in drugo"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Zasebni prostor"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-sq-v35/strings.xml b/SafetyCenter/Resources/res/values-sq-v35/strings.xml
new file mode 100644
index 000000000..cec6e3726
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-sq-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Siguria e rrjetit celular"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Lloji i rrjetit, enkriptimi, kontrollet e njoftimeve"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privatësia"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Kontrollet e privatësisë"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Lejet, kontrollet"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Hapësira private"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Konfiguro \"Hapësirën private\" dhe të tjera"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Hapësira private"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-sr-v35/strings.xml b/SafetyCenter/Resources/res/values-sr-v35/strings.xml
new file mode 100644
index 000000000..65b50b1ce
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-sr-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Безбедност мобилне мреже"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тип мреже, шифровање, контроле обавештења"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Приватност"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Контроле приватности"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Дозволе, контроле"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Приватни простор"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Подесите приватни простор и друго"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Приватни простор"</string>
+</resources>
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-sv-v35/strings.xml b/SafetyCenter/Resources/res/values-sv-v35/strings.xml
new file mode 100644
index 000000000..77a6de01f
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-sv-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Säkerhet för mobilnätverk"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Nätverkstyp, kryptering, aviseringsinställningar"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Integritet"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Integritetsinställningar"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Behörigheter, inställningar"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Privat rum"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Ställ in privat rum med mera"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Privat rum"</string>
+</resources>
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-sw-v35/strings.xml b/SafetyCenter/Resources/res/values-sw-v35/strings.xml
new file mode 100644
index 000000000..dc5283636
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-sw-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Usalama wa mtandao wa simu"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Aina ya mtandao, usimbaji fiche, vidhibiti vya arifa"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Faragha"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Vidhibiti vya faragha"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Ruhusa, vidhibiti"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Nafasi ya Faragha"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Weka mipangilio ya Nafasi ya Faragha na zaidi"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Nafasi ya Faragha"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ta-v35/strings.xml b/SafetyCenter/Resources/res/values-ta-v35/strings.xml
new file mode 100644
index 000000000..8eb41ce20
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ta-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"மொபைல் நெட்வொர்க் பாதுகாப்பு"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"நெட்வொர்க் வகை, என்க்ரிப்ஷன், அறிவிப்புக் கட்டுப்பாடுகள்"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"தனியுரிமை"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"தனியுரிமைக் கட்டுப்பாடுகள்"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"அனுமதிகள், கட்டுப்பாடுகள்"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"தனிப்பட்ட சேமிப்பிடம்"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"தனிப்பட்ட சேமிப்பிடத்தை அமைக்கலாம் மற்றும் பலவற்றைச் செய்யலாம்"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"தனிப்பட்ட சேமிப்பிடம்"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-te-v35/strings.xml b/SafetyCenter/Resources/res/values-te-v35/strings.xml
new file mode 100644
index 000000000..fb4a0812a
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-te-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"సెల్యులర్ నెట్‌వర్క్ సెక్యూరిటీ"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"నెట్‌వర్క్ రకం, ఎన్‌క్రిప్షన్, నోటిఫికేషన్ కంట్రోల్స్"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"గోప్యత"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"గోప్యతా కంట్రోల్స్"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"అనుమతులు, కంట్రోల్స్"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"ప్రైవేట్ స్పేస్"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ప్రైవేట్ స్పేస్‌ను సెటప్ చేయండి, మరెన్నింటినో చేయండి"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"ప్రైవేట్ స్పేస్"</string>
+</resources>
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-th-v35/strings.xml b/SafetyCenter/Resources/res/values-th-v35/strings.xml
new file mode 100644
index 000000000..193bb5497
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-th-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"การรักษาความปลอดภัยของเครือข่ายมือถือ"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"ประเภทเครือข่าย การเข้ารหัส ส่วนควบคุมการแจ้งเตือน"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"ความเป็นส่วนตัว"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"การควบคุมความเป็นส่วนตัว"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"สิทธิ์ การควบคุม"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"พื้นที่ส่วนตัว"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"ตั้งค่าพื้นที่ส่วนตัวและอื่นๆ"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"พื้นที่ส่วนตัว"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-tl-v35/strings.xml b/SafetyCenter/Resources/res/values-tl-v35/strings.xml
new file mode 100644
index 000000000..c0c43dea9
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-tl-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Seguridad ng cellular network"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Network type, pag-encrypt, mga kontrol sa notification"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Privacy"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Mga kontrol sa privacy"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Mga pahintulot, kontrol"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Pribadong Space"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"I-set up ang Pribadong Space, at higit pa"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Pribadong Space"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-tr-v35/strings.xml b/SafetyCenter/Resources/res/values-tr-v35/strings.xml
new file mode 100644
index 000000000..89a3a3e0c
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-tr-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Hücresel ağ güvenliği"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Ağ türü, şifreleme, bildirim kontrolleri"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Gizlilik"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Gizlilik denetimleri"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"İzinler, denetimler"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Gizli Alan"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Gizli alan yapılandırma ve daha fazlası"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Gizli Alan"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-uk-v35/strings.xml b/SafetyCenter/Resources/res/values-uk-v35/strings.xml
new file mode 100644
index 000000000..0decae7b9
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-uk-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Безпека мобільної мережі"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Тип мережі, шифрування, налаштування сповіщень"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Конфіденційність"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Налаштування конфіденційності"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Дозволи, налаштування"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Приватний простір"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Налаштуйте приватний простір тощо"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Приватний простір"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-ur-v35/strings.xml b/SafetyCenter/Resources/res/values-ur-v35/strings.xml
new file mode 100644
index 000000000..d4184bda3
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-ur-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"سیلولر نیٹ ورک سیکیورٹی"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"نیٹ ورک کی قسم، مرموز کاری، نوٹیفکیشن کنٹرولز"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"رازداری"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"رازداری سے متعلق کنٹرولز"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"اجازتیں، کنٹرولز"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"نجی اسپیس"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"نجی اسپیس اور بھی بہت کچھ سیٹ اپ کریں"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"نجی اسپیس"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-uz-v35/strings.xml b/SafetyCenter/Resources/res/values-uz-v35/strings.xml
new file mode 100644
index 000000000..58645fd60
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-uz-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Mobil tarmoq xavfsizligi"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Tarmoq turi, shifrlash, bildirishnomalar boshqaruvi"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Maxfiylik"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Maxfiylik sozlamalari"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Ruxsatlar, parametrlar"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Maxfiy joy"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Maxfiy joyni sozlash"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Maxfiy joy"</string>
+</resources>
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-v35/config.xml b/SafetyCenter/Resources/res/values-v35/config.xml
new file mode 100644
index 000000000..3d8ac11ea
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-v35/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Comma separated list of safety source IDs to show in the same task as the safety center -->
+ <string name="config_same_task_safety_source_ids" translatable="false">AndroidAccessibility,AndroidBackgroundLocation,AndroidBiometrics,AndroidHealthConnect,AndroidLockScreen,AndroidPrivateSpace,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,AndroidPrivateSpace,TestSource</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-v35/strings.xml b/SafetyCenter/Resources/res/values-v35/strings.xml
new file mode 100644
index 000000000..14bccdb93
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-v35/strings.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Cellular Network Security -->
+ <string name="cellular_network_security_title" description="The title of the group of safety settings relating to cellular network security">Cellular network security</string>
+ <string name="cellular_network_security_summary" description="The summary of the group of safety settings relating to cellular network security, which describes the group contents">Network type, encryption, notification controls</string>
+
+ <!-- Device unlock -->
+ <string name="biometrics_title_for_private_profile" description="The default title of the setting for managing biometric options on the device for private space"><!-- Empty placeholder--></string>
+
+ <!-- Privacy -->
+ <string name="privacy_title" description="The title of the group of safety settings relating to privacy">Privacy</string>
+ <string name="privacy_sources_title" description="The title of the group of safety settings relating to privacy controls">Privacy controls</string>
+ <string name="privacy_sources_summary" description="The summary of the group of safety settings relating to privacy, which describes the group contents">Permissions, controls</string>
+ <string name="privacy_additional_title" description="The header, or blank if none wanted, for the additional privacy settings on the main page"></string>
+
+ <!-- More settings -->
+ <string name="private_space_title" description="The title of the entry for Private Space">Private Space</string>
+ <string name="private_space_summary" description="The summary of the entry for Private Space settings, which describes the page contents">Setup Private Space, and more</string>
+ <string name="private_space_search_terms" description="Search keywords of the entry for Private Space settings">Private Space</string>
+
+</resources>
diff --git a/SafetyCenter/Resources/res/values-vi-v35/strings.xml b/SafetyCenter/Resources/res/values-vi-v35/strings.xml
new file mode 100644
index 000000000..9ae5311e9
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-vi-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"An ninh mạng di động"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Loại mạng, quy trình mã hoá, quyền kiểm soát thông báo"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Quyền riêng tư"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Chế độ kiểm soát quyền riêng tư"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Quyền truy cập, chế độ kiểm soát"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Không gian riêng tư"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Thiết lập Không gian riêng tư và các tính năng khác"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Không gian riêng tư"</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 ff50335d8..da468fa0e 100644
--- a/SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rCN-v34/strings.xml
@@ -22,7 +22,7 @@
<string name="privacy_sources_summary" msgid="4083646673569677049">"权限、信息中心和控件"</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_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-rCN-v35/strings.xml b/SafetyCenter/Resources/res/values-zh-rCN-v35/strings.xml
new file mode 100644
index 000000000..a58f0b2d6
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-zh-rCN-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"移动网络安全性"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"网络类型、加密、通知控件"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"隐私"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"隐私控制"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"权限、控制"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"私密空间"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"设置私密空间等"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"私密空间"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml
index c850335ad..9da0c7694 100644
--- a/SafetyCenter/Resources/res/values-zh-rHK-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rHK-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-zh-rHK-v35/strings.xml b/SafetyCenter/Resources/res/values-zh-rHK-v35/strings.xml
new file mode 100644
index 000000000..b234389e1
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-zh-rHK-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"流動網絡安全性"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"網絡類型、加密、通知控制項"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"私隱"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"私隱權設定"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"權限、控制項"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"私人空間"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"設定「私人空間」等項目"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"私人空間"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
index 09f8e9d5d..54bbf0f1c 100644
--- a/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
+++ b/SafetyCenter/Resources/res/values-zh-rTW-v34/strings.xml
@@ -22,7 +22,7 @@
<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>
diff --git a/SafetyCenter/Resources/res/values-zh-rTW-v35/strings.xml b/SafetyCenter/Resources/res/values-zh-rTW-v35/strings.xml
new file mode 100644
index 000000000..8401b9f47
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-zh-rTW-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"行動網路安全性"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"網路類型、加密、通知控制選項"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"隱私權"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"隱私權控制項"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"權限、控制選項"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"私人空間"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"設定私人空間等項目"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"私人空間"</string>
+</resources>
diff --git a/SafetyCenter/Resources/res/values-zu-v35/strings.xml b/SafetyCenter/Resources/res/values-zu-v35/strings.xml
new file mode 100644
index 000000000..39053e960
--- /dev/null
+++ b/SafetyCenter/Resources/res/values-zu-v35/strings.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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="cellular_network_security_title" msgid="2986431282931510973">"Ukuphepha kwenethiwekhi yeselula"</string>
+ <string name="cellular_network_security_summary" msgid="7319307247487475572">"Uhlobo lwenethiwekhi, ukubethela, izilawuli zezaziso"</string>
+ <string name="biometrics_title_for_private_profile" msgid="542819107383037820"></string>
+ <string name="privacy_title" msgid="7047524783080782769">"Ubumfihlo"</string>
+ <string name="privacy_sources_title" msgid="309304028326660956">"Izilawuli zobumfihlo"</string>
+ <string name="privacy_sources_summary" msgid="2165270848857537278">"Izimvume, izilawuli"</string>
+ <string name="privacy_additional_title" msgid="4239060639056083649"></string>
+ <string name="private_space_title" msgid="6158245041481535879">"Isikhala Esiyimfihlo"</string>
+ <string name="private_space_summary" msgid="529869826714610294">"Setha Isikhala Esiyimfihlo, nokunye"</string>
+ <string name="private_space_search_terms" msgid="4820808478299116258">"Isikhala Esiyimfihlo"</string>
+</resources>
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 16b896569..0ff9ebfb8 100644
--- a/SafetyCenter/Resources/shared_res/values-de/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-de/strings.xml
@@ -40,7 +40,6 @@
<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>
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 3fb167037..4ad0a331c 100644
--- a/SafetyCenter/Resources/shared_res/values-pa/strings.xml
+++ b/SafetyCenter/Resources/shared_res/values-pa/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-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/Android.bp b/SafetyCenter/ResourcesLib/Android.bp
index ebc7aa017..4829dbc40 100644
--- a/SafetyCenter/ResourcesLib/Android.bp
+++ b/SafetyCenter/ResourcesLib/Android.bp
@@ -16,6 +16,7 @@
// Library to manage all the Safety Center overlayable resources.
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
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/Android.bp b/SafetyCenter/ResourcesLib/tests/Android.bp
index 75fead776..18d98ccb6 100644
--- a/SafetyCenter/ResourcesLib/tests/Android.bp
+++ b/SafetyCenter/ResourcesLib/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
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/Android.bp b/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/Android.bp
index 18628805d..7306ee247 100644
--- a/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/Android.bp
+++ b/SafetyCenter/ResourcesLib/tests/SafetyCenterResourcesLibTestResources/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
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/SafetyLabel/Android.bp b/SafetyLabel/Android.bp
index 119890d3f..70307242d 100644
--- a/SafetyLabel/Android.bp
+++ b/SafetyLabel/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -46,4 +47,3 @@ java_library {
"//packages/modules/Permission:__subpackages__",
],
}
-
diff --git a/SafetyLabel/tests/Android.bp b/SafetyLabel/tests/Android.bp
index 2026a6ac8..613eda2c6 100644
--- a/SafetyLabel/tests/Android.bp
+++ b/SafetyLabel/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index aa594e81e..524d02af5 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -6,7 +6,76 @@
],
"carpermission-presubmit" : [
{
- "name" : "CtsPermission3TestCases"
+ "name" : "CtsPermissionUiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "carpermission-postsubmit" : [
+ {
+ "name" : "CtsPermissionUiTestCases",
+ "options": [
+ {
+ "include-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "alltests" : [
+ {
+ "name" : "PermissionControllerMockingTests"
+ },
+ {
+ "name" : "CtsPermissionTestCases"
+ },
+ {
+ "name" : "CtsPermissionUiTestCases"
+ },
+ {
+ "name" : "CtsPermissionPolicyTestCases"
+ },
+ {
+ "name" : "CtsAppOpsTestCases"
+ },
+ {
+ "name" : "CtsAppOps2TestCases"
+ },
+ {
+ "name": "PermissionControllerOutOfProcessTests"
+ },
+ {
+ "name" : "CtsRoleTestCases"
+ },
+ {
+ "name" : "CtsPermissionMultiUserTestCases"
+ },
+ {
+ "name" : "CtsPermissionMultiDeviceTestCases"
+ },
+ {
+ "name": "CtsBackupTestCases",
+ "options": [
+ {
+ "include-filter": "android.backup.cts.PermissionTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsDevicePolicyManagerTestCases",
+ "options": [
+ {
+ "include-annotation": "com.android.cts.devicepolicy.annotations.PermissionsTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit" : [
+ {
+ // TODO(b/332974906): Replace in alltests.
+ "name" : "CtsDevicePolicyManagerTestCases_Permissions"
}
]
}
diff --git a/flags/Android.bp b/flags/Android.bp
new file mode 100644
index 000000000..457aa4f91
--- /dev/null
+++ b/flags/Android.bp
@@ -0,0 +1,60 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aconfig_declarations {
+ name: "com.android.permission.flags-aconfig",
+ container: "com.android.permission",
+ package: "com.android.permission.flags",
+ exportable: true,
+ srcs: ["flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.permission.flags-aconfig-java-export",
+ aconfig_declarations: "com.android.permission.flags-aconfig",
+ mode: "exported",
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ installable: false,
+ libs: ["framework-configinfrastructure"],
+ visibility: [
+ "//packages/modules/Permission:__subpackages__",
+ ],
+ apex_available: [
+ "com.android.permission",
+ "test_com.android.permission",
+ ],
+}
+
+java_aconfig_library {
+ name: "com.android.permission.flags-aconfig-java",
+ aconfig_declarations: "com.android.permission.flags-aconfig",
+ sdk_version: "module_current",
+ min_sdk_version: "30",
+ installable: false,
+ libs: ["framework-configinfrastructure"],
+ visibility: [
+ "//packages/modules/Permission:__subpackages__",
+ ],
+ apex_available: [
+ "com.android.permission",
+ "test_com.android.permission",
+ ],
+}
diff --git a/flags/flags.aconfig b/flags/flags.aconfig
new file mode 100644
index 000000000..a81de8144
--- /dev/null
+++ b/flags/flags.aconfig
@@ -0,0 +1,47 @@
+package: "com.android.permission.flags"
+container: "com.android.permission"
+
+flag {
+ name: "private_profile_supported"
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag is used to support private profile in safety center"
+ bug: "286539356"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "private_profile_title_api"
+ is_exported: true
+ namespace: "permissions"
+ description: "This flag is used to guard the private profile title api in safety center"
+ bug: "286539356"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "wear_privacy_dashboard_enabled_read_only"
+ is_exported: true
+ namespace: "wear_security"
+ description: "This flag is used to support Privacy dashboard for Wear"
+ bug: "309721061"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "archiving_read_only"
+ is_exported: true
+ namespace: "permissions"
+ description: "Feature flag to enable the archiving feature."
+ bug: "278553670"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "add_banners_to_privacy_sensitive_apps_for_aaos"
+ is_exported: true
+ namespace: "permissions"
+ description: "Flag to display warning banners to privacy sensitive apps in AAOS."
+ bug: "327489942"
+ is_fixed_read_only: true
+}
diff --git a/framework-s/Android.bp b/framework-s/Android.bp
index e017a7ea5..f0a156ae8 100644
--- a/framework-s/Android.bp
+++ b/framework-s/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -44,6 +45,7 @@ java_library {
name: "framework-permission-s-shared",
srcs: [":framework-permission-s-shared-srcs"],
libs: [
+ "error_prone_annotations",
"framework-annotations-lib",
"unsupportedappusage",
],
@@ -54,6 +56,14 @@ java_library {
installable: false,
min_sdk_version: "30",
sdk_version: "module_current",
+ lint: {
+ baseline_filename: "lint-baseline.xml",
+ },
+}
+
+platform_compat_config {
+ name: "framework-permission-s-compat-config",
+ src: ":framework-permission-s",
}
java_sdk_library {
@@ -64,11 +74,14 @@ java_sdk_library {
],
libs: [
"androidx.annotation_annotation",
+ "app-compat-annotations",
"framework-annotations-lib",
],
static_libs: [
"framework-permission-s-shared",
"modules-utils-build",
+ "android.permission.flags-aconfig-java-export",
+ "com.android.permission.flags-aconfig-java",
],
apex_available: [
"com.android.permission",
@@ -82,23 +95,19 @@ java_sdk_library {
],
installable: true,
jarjar_rules: "jarjar-rules.txt",
- lint: {
- strict_updatability_linting: true,
- },
min_sdk_version: "31",
permitted_packages: [
"android.permission",
"android.app.role",
+ "android.app.ecm",
"android.safetycenter",
"android.safetylabel",
],
-}
-
-java_api_contribution {
- name: "framework-permission-s-public-stubs",
- api_surface: "public",
- api_file: "api/current.txt",
- visibility: [
- "//build/orchestrator/apis",
+ aconfig_declarations: [
+ "android.permission.flags-aconfig",
+ "com.android.permission.flags-aconfig",
],
+ lint: {
+ baseline_filename: "lint-baseline-framework-permission-s.xml",
+ },
}
diff --git a/framework-s/api/current.txt b/framework-s/api/current.txt
index d54af92f5..d943a03a1 100644
--- a/framework-s/api/current.txt
+++ b/framework-s/api/current.txt
@@ -14,6 +14,7 @@ package android.app.role {
field public static final String ROLE_HOME = "android.app.role.HOME";
field public static final String ROLE_NOTES = "android.app.role.NOTES";
field public static final String ROLE_SMS = "android.app.role.SMS";
+ field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final String ROLE_WALLET = "android.app.role.WALLET";
}
}
diff --git a/framework-s/api/module-lib-current.txt b/framework-s/api/module-lib-current.txt
index 80f1cde45..97a8623b3 100644
--- a/framework-s/api/module-lib-current.txt
+++ b/framework-s/api/module-lib-current.txt
@@ -1,4 +1,12 @@
// Signature format: 2.0
+package android.app.ecm {
+
+ @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public class EnhancedConfirmationFrameworkInitializer {
+ method public static void registerServiceWrappers();
+ }
+
+}
+
package android.app.role {
public class RoleFrameworkInitializer {
@@ -7,6 +15,7 @@ package android.app.role {
public final class RoleManager {
method @Nullable public String getBrowserRoleHolder(int);
+ method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyRoleHolder(int);
method @Nullable public String getSmsRoleHolder(int);
method @Nullable @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public boolean setBrowserRoleHolder(@Nullable String, int);
}
diff --git a/framework-s/api/module-lib-lint-baseline.txt b/framework-s/api/module-lib-lint-baseline.txt
new file mode 100644
index 000000000..1445d9814
--- /dev/null
+++ b/framework-s/api/module-lib-lint-baseline.txt
@@ -0,0 +1,39 @@
+// Baseline format: 1.0
+BroadcastBehavior: android.safetycenter.SafetyCenterManager#ACTION_REFRESH_SAFETY_SOURCES:
+ Field 'ACTION_REFRESH_SAFETY_SOURCES' is missing @BroadcastBehavior
+BroadcastBehavior: android.safetycenter.SafetyCenterManager#ACTION_SAFETY_CENTER_ENABLED_CHANGED:
+ Field 'ACTION_SAFETY_CENTER_ENABLED_CHANGED' is missing @BroadcastBehavior
+
+
+MissingPermission: android.app.role.RoleManager#addRoleHolderFromController(String, String):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.addRoleHolderFromController(String, String) is hidden or removed
+MissingPermission: android.app.role.RoleManager#getHeldRolesFromController(String):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.getHeldRolesFromController(String) is hidden or removed
+MissingPermission: android.app.role.RoleManager#removeRoleHolderFromController(String, String):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.removeRoleHolderFromController(String, String) is hidden or removed
+MissingPermission: android.app.role.RoleManager#setRoleNamesFromController(java.util.List<java.lang.String>):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.setRoleNamesFromController(java.util.List<java.lang.String>) is hidden or removed
+
+
+RequiresPermission: android.app.role.RoleManager#addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle):
+ Method 'addOnRoleHoldersChangedListenerAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#addRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'addRoleHolderAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#addRoleHolderFromController(String, String):
+ Method 'addRoleHolderFromController' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#clearRoleHoldersAsUser(String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'clearRoleHoldersAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#getRoleHolders(String):
+ Method 'getRoleHolders' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#getRoleHoldersAsUser(String, android.os.UserHandle):
+ Method 'getRoleHoldersAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle):
+ Method 'removeOnRoleHoldersChangedListenerAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'removeRoleHolderAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#removeRoleHolderFromController(String, String):
+ Method 'removeRoleHolderFromController' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#setBrowserRoleHolder(String, int):
+ Method 'setBrowserRoleHolder' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#setRoleNamesFromController(java.util.List<java.lang.String>):
+ Method 'setRoleNamesFromController' documentation mentions permissions already declared by @RequiresPermission
diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt
index 9545356a4..e15887576 100644
--- a/framework-s/api/system-current.txt
+++ b/framework-s/api/system-current.txt
@@ -1,4 +1,17 @@
// Signature format: 2.0
+package android.app.ecm {
+
+ @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public final class EnhancedConfirmationManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public void clearRestriction(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @NonNull public android.content.Intent createRestrictedSettingDialogIntent(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public boolean isClearRestrictionAllowed(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public boolean isRestricted(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES) public void setClearRestrictionAllowed(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+ field public static final String ACTION_SHOW_ECM_RESTRICTED_SETTING_DIALOG = "android.app.ecm.action.SHOW_ECM_RESTRICTED_SETTING_DIALOG";
+ }
+
+}
+
package android.app.role {
public interface OnRoleHoldersChangedListener {
@@ -10,6 +23,7 @@ package android.app.role {
method @Deprecated @WorkerThread public abstract boolean onAddRoleHolder(@NonNull String, @NonNull String, int);
method @Deprecated @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
method @Deprecated @WorkerThread public abstract boolean onClearRoleHolders(@NonNull String, int);
+ method @Deprecated @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @NonNull public java.util.List<java.lang.String> onGetLegacyFallbackDisabledRoles();
method @Deprecated @WorkerThread public abstract boolean onGrantDefaultRoles();
method @Deprecated public abstract boolean onIsApplicationQualifiedForRole(@NonNull String, @NonNull String);
method @Deprecated public boolean onIsApplicationVisibleForRole(@NonNull String, @NonNull String);
@@ -29,12 +43,14 @@ package android.app.role {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isBypassingRoleQualification();
+ method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean isRoleFallbackEnabled(@NonNull String);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.BYPASS_ROLE_QUALIFICATION) public void setBypassingRoleQualification(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_DEFAULT_APPLICATIONS) public void setDefaultApplication(@NonNull String, @Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void setRoleFallbackEnabled(@NonNull String, boolean);
method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
field public static final String ROLE_DEVICE_POLICY_MANAGEMENT = "android.app.role.DEVICE_POLICY_MANAGEMENT";
@@ -555,6 +571,7 @@ package android.safetycenter.config {
method public int getProfile();
method @StringRes public int getSearchTermsResId();
method @StringRes public int getSummaryResId();
+ method @FlaggedApi("com.android.permission.flags.private_profile_title_api") @StringRes public int getTitleForPrivateProfileResId();
method @StringRes public int getTitleForWorkResId();
method @StringRes public int getTitleResId();
method public int getType();
@@ -590,6 +607,7 @@ package android.safetycenter.config {
method @NonNull public android.safetycenter.config.SafetySource.Builder setRefreshOnPageOpenAllowed(boolean);
method @NonNull public android.safetycenter.config.SafetySource.Builder setSearchTermsResId(@StringRes int);
method @NonNull public android.safetycenter.config.SafetySource.Builder setSummaryResId(@StringRes int);
+ method @FlaggedApi("com.android.permission.flags.private_profile_title_api") @NonNull public android.safetycenter.config.SafetySource.Builder setTitleForPrivateProfileResId(@StringRes int);
method @NonNull public android.safetycenter.config.SafetySource.Builder setTitleForWorkResId(@StringRes int);
method @NonNull public android.safetycenter.config.SafetySource.Builder setTitleResId(@StringRes int);
}
diff --git a/framework-s/api/system-lint-baseline.txt b/framework-s/api/system-lint-baseline.txt
new file mode 100644
index 000000000..5bf9a0a11
--- /dev/null
+++ b/framework-s/api/system-lint-baseline.txt
@@ -0,0 +1,37 @@
+// Baseline format: 1.0
+BroadcastBehavior: android.safetycenter.SafetyCenterManager#ACTION_REFRESH_SAFETY_SOURCES:
+ Field 'ACTION_REFRESH_SAFETY_SOURCES' is missing @BroadcastBehavior
+BroadcastBehavior: android.safetycenter.SafetyCenterManager#ACTION_SAFETY_CENTER_ENABLED_CHANGED:
+ Field 'ACTION_SAFETY_CENTER_ENABLED_CHANGED' is missing @BroadcastBehavior
+
+
+MissingPermission: android.app.role.RoleManager#addRoleHolderFromController(String, String):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.addRoleHolderFromController(String, String) is hidden or removed
+MissingPermission: android.app.role.RoleManager#getHeldRolesFromController(String):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.getHeldRolesFromController(String) is hidden or removed
+MissingPermission: android.app.role.RoleManager#removeRoleHolderFromController(String, String):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.removeRoleHolderFromController(String, String) is hidden or removed
+MissingPermission: android.app.role.RoleManager#setRoleNamesFromController(java.util.List<java.lang.String>):
+ Permission PERMISSION_MANAGE_ROLES_FROM_CONTROLLER required by method android.app.role.RoleManager.setRoleNamesFromController(java.util.List<java.lang.String>) is hidden or removed
+
+
+RequiresPermission: android.app.role.RoleManager#addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle):
+ Method 'addOnRoleHoldersChangedListenerAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#addRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'addRoleHolderAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#addRoleHolderFromController(String, String):
+ Method 'addRoleHolderFromController' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#clearRoleHoldersAsUser(String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'clearRoleHoldersAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#getRoleHolders(String):
+ Method 'getRoleHolders' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#getRoleHoldersAsUser(String, android.os.UserHandle):
+ Method 'getRoleHoldersAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle):
+ Method 'removeOnRoleHoldersChangedListenerAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+ Method 'removeRoleHolderAsUser' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#removeRoleHolderFromController(String, String):
+ Method 'removeRoleHolderFromController' documentation mentions permissions already declared by @RequiresPermission
+RequiresPermission: android.app.role.RoleManager#setRoleNamesFromController(java.util.List<java.lang.String>):
+ Method 'setRoleNamesFromController' documentation mentions permissions already declared by @RequiresPermission
diff --git a/framework-s/jarjar-rules.txt b/framework-s/jarjar-rules.txt
index 3b888fe99..780410b7b 100644
--- a/framework-s/jarjar-rules.txt
+++ b/framework-s/jarjar-rules.txt
@@ -1,4 +1,12 @@
rule android.os.HandlerExecutor android.permission.jarjar.@0
+rule android.permission.flags.*FeatureFlags* android.permission.jarjar.@0
+rule android.permission.flags.FeatureFlags* android.permission.jarjar.@0
+rule android.permission.flags.FeatureFlags android.permission.jarjar.@0
+rule android.permission.flags.Flags android.permission.jarjar.@0
rule android.util.IndentingPrintWriter android.permission.jarjar.@0
rule com.android.internal.** android.permission.jarjar.@0
rule com.android.modules.** android.permission.jarjar.@0
+rule com.android.permission.flags.*FeatureFlags* android.permission.jarjar.@0
+rule com.android.permission.flags.FeatureFlags* android.permission.jarjar.@0
+rule com.android.permission.flags.FeatureFlags android.permission.jarjar.@0
+rule com.android.permission.flags.Flags android.permission.jarjar.@0
diff --git a/framework-s/java/android/app/ecm/EnhancedConfirmationFrameworkInitializer.java b/framework-s/java/android/app/ecm/EnhancedConfirmationFrameworkInitializer.java
new file mode 100644
index 000000000..1a42f7ee2
--- /dev/null
+++ b/framework-s/java/android/app/ecm/EnhancedConfirmationFrameworkInitializer.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.app.ecm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.annotation.TargetApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+import android.os.Build;
+import android.permission.flags.Flags;
+
+/**
+ * Class holding initialization code for enhanced confirmation code in the permission module.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class EnhancedConfirmationFrameworkInitializer {
+ private EnhancedConfirmationFrameworkInitializer() {}
+
+ /**
+ * Called by {@link SystemServiceRegistry}'s static initializer and registers
+ * {@link EnhancedConfirmationManager} to {@link Context}, so that
+ * {@link Context#getSystemService} can return it.
+ *
+ * <p>If this is called from other places, it throws a {@link IllegalStateException}.
+ */
+ public static void registerServiceWrappers() {
+ SystemServiceRegistry.registerContextAwareService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE,
+ EnhancedConfirmationManager.class,
+ (context, serviceBinder) -> new EnhancedConfirmationManager(context,
+ IEnhancedConfirmationManager.Stub.asInterface(serviceBinder)));
+ }
+}
diff --git a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java
new file mode 100644
index 000000000..74062165e
--- /dev/null
+++ b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java
@@ -0,0 +1,356 @@
+/*
+ * 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 android.app.ecm;
+
+import static android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.RemoteException;
+import android.permission.flags.Flags;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+
+/**
+ * This class provides the core API for ECM (Enhanced Confirmation Mode). ECM is a feature that
+ * restricts access to protected **settings** (i.e., sensitive resources) by restricted **apps**
+ * (apps from from dangerous sources, such as sideloaded packages or packages downloaded from a web
+ * browser).
+ *
+ * <p>Specifically, this class provides the ability to:
+ *
+ * <ol>
+ * <li>Check whether a setting is restricted from an app ({@link #isRestricted})
+ * <li>Get an intent that will open the "Restricted setting" dialog ({@link
+ * #createRestrictedSettingDialogIntent}) (a dialog that informs the user that the operation
+ * they've attempted to perform is restricted)
+ * <li>Check whether an app is eligible to have its restriction status cleared ({@link
+ * #isClearRestrictionAllowed})
+ * <li>Clear an app's restriction status (i.e., un-restrict it). ({@link #clearRestriction})
+ * </ol>
+ *
+ * <p>Methods of this class will generally accept an app (identified by a packageName and a user)
+ * and a "setting" (a string representing the "sensitive resource") as arguments. ECM's exact
+ * behavior will generally depend on what restriction state ECM considers each setting and app. For
+ * example:
+ *
+ * <ol>
+ * <li>A setting may be considered by ECM to be either **protected** or **not protected**. In
+ * general, this should be considered hardcoded into ECM's implementation: nothing can
+ * "protect" or "unprotect" a setting.
+ * <li>An app may be considered as being **not restricted** or **restricted**. A restricted app
+ * will be restricted from accessing all protected settings. Whether ECM considers any
+ * particular app restricted is an implementation detail of ECM. However, the user is able to
+ * clear any restricted app's restriction status (i.e, un-restrict it), after which ECM will
+ * consider the app **not restricted**.
+ * </ol>
+ *
+ * Why is ECM needed? Consider the following (pre-ECM) scenario:
+ *
+ * <ol>
+ * <li>The user downloads and installs an apk file from a browser.
+ * <li>The user opens Settings -> Accessibility
+ * <li>The user tries to register the app as an accessibility service.
+ * <li>The user is shown a permission prompt "Allow _ to have full control of your device?"
+ * <li>The user clicks "Allow"
+ * <li>The downloaded app now has full control of the device.
+ * </ol>
+ *
+ * The purpose of ECM is to add more friction to this scenario.
+ *
+ * <p>With ECM, this scenario becomes:
+ *
+ * <ol>
+ * <li>The user downloads and installs an apk file from a browser.
+ * <li>The user goes into Settings -> Accessibility.
+ * <li>The user tries to register the app as an accessibility service.
+ * <li>The user is presented with a "Restricted setting" dialog explaining that the attempted
+ * action has been restricted. (No "allow" button is shown, but a link is given to a screen
+ * with intentionally-obscure instructions on how to proceed.)
+ * <li>The user must now navigate to Settings -> Apps -> [app]
+ * <li>The user then must click on "..." (top-right corner hamburger menu), then click "Allow
+ * restricted settings"
+ * <li>The user goes (again) into Settings -> Accessibility and (again) tries to register the app
+ * as an accessibility service.
+ * <li>The user is shown a permission prompt "Allow _ to have full control of your device?"
+ * <li>The user clicks "Allow"
+ * <li>The downloaded app now has full control of the device.
+ * </ol>
+ *
+ * And, expanding on the above scenario, the role that this class plays is as follows:
+ *
+ * <ol>
+ * <li>The user downloads and installs an apk file from a browser.
+ * <li>The user goes into Settings -> Accessibility.
+ * <p>**This screen then calls {@link #isRestricted}, which checks whether each app listed
+ * on-screen is restricted from the accessibility service setting. It uses this to visually
+ * "gray out" restricted apps.**
+ * <li>The user tries to register the app as an accessibility service.
+ * <p>**This screen then calls {@link #createRestrictedSettingDialogIntent} and starts the
+ * intent. This opens the "Restricted setting" dialog.**
+ * <li>The user is presented with a "Restricted setting" dialog explaining that the attempted
+ * action is restricted. (No "allow" button is shown, but a link is given to a screen with
+ * intentionally-obscure instructions on how to proceed.)
+ * <p>**Upon opening, this dialog marks the app as eligible to have its restriction status
+ * cleared.**
+ * <li>The user must now navigate to Settings -> Apps -> [app].
+ * <p>**This screen calls {@link #isClearRestrictionAllowed} to check whether the app is
+ * eligible to have its restriction status cleared. If this returns {@code true}, this screen
+ * should then show a "Allow restricted setting" button inside the top-right hamburger menu.**
+ * <li>The user then must click on "..." (top-right corner hamburger menu), then click "Allow
+ * restricted settings".
+ * <p>**In response, this screen should now call {@link #clearRestriction}.**
+ * <li>The user goes (again) into Settings -> Accessibility and (again) tries to register the app
+ * as an accessibility service.
+ * <li>The user is shown a permission prompt "Allow _ to have full control of your device?"
+ * <li>The user clicks "Allow"
+ * <li>The downloaded app now has full control of the device.
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@SystemService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE)
+public final class EnhancedConfirmationManager {
+ /*
+ * At the API level, we use the following terminology:
+ *
+ * - The capability of an app to access a setting may be considered (by ECM) to be *restricted*
+ * or *not restricted*.
+ * - A setting may be considered (by ECM) to be *protected* or *not protected*.
+ * - The state of an app may be considered (by ECM) to be *restricted* or *not restricted*
+ *
+ * In this implementation, however, the state of an app is considered either **guarded** or
+ * **not guarded**; these terms can generally be considered synonymous with **restricted** and
+ * **not restricted**. (Keeping in mind that, the capability of any app to access any
+ * non-protected setting will always be considered "not restricted", even if the state of the
+ * app is considered "restricted".). An app can also be in a third state: **guarded and
+ * acknowledged**, which corresponds with an app that is restricted and is eligible to have its
+ * restriction status cleared.
+ *
+ * Currently, the ECM state of any given app is stored in the OP_ACCESS_RESTRICTED_SETTINGS
+ * appop (though this may change in the future):
+ *
+ * - MODE_ALLOWED means the app is explicitly **not guarded**. (U- default)
+ * - MODE_ERRORED means the app is explicitly **guarded**. (Only settable in U-.)
+ * - MODE_IGNORED means the app is explicitly **guarded and acknowledged**. (An app enters this
+ * state as soon as the "Restricted setting" dialog has been shown to the user. If an app is
+ * in this state, Settings is now allowed to provide the user with the option to clear the
+ * restriction.)
+ * - MODE_DEFAULT means the app's ECM state should be decided lazily. (V+ default) (That is,
+ * each time a caller checks whether or not an app is considered guarded by ECM, we'll run an
+ * heuristic to determine this.)
+ *
+ * Some notes on compatibility:
+ *
+ * - On U-, MODE_ALLOWED is the default mode of OP_ACCESS_RESTRICTED_SETTINGS. On both U- and
+ * V+, this is also the mode after the app's restriction has been cleared.
+ * - In U-, the mode needed to be explicitly set (for example, by a browser that allows a
+ * dangerous app to be installed) to MODE_ERRORED to indicate that an app is guarded. In V+,
+ * we no longer allow an app to be placed into MODE_ERRORED, but for compatibility, we still
+ * recognize MODE_ERRORED to indicate that an app is explicitly guarded.
+ * - In V+, the default mode is MODE_DEFAULT. Unlike U-, this potentially affects *all* apps,
+ * not just the ones which have been explicitly marked as **guarded**.
+ *
+ * Regarding ECM "setting"s: a setting may be any abstract resource identified by a string. ECM
+ * may consider any particular setting **protected** or **not protected**. For now, the set of
+ * protected settings is hardcoded, but this may evolve in the future.
+ *
+ * TODO(b/320512579): These methods currently enforce UPDATE_APP_OPS_STATS,
+ * UPDATE_APP_OPS_STATS, and, for setter methods, MANAGE_APP_OPS_MODES. We should add
+ * RequiresPermission annotations, but we can't, because some of these permissions are hidden
+ * API. Either upgrade these to SystemApi or enforce a different permission, then add the
+ * appropriate RequiresPermission annotation.
+ */
+
+ /**
+ * Shows the "Restricted setting" dialog. Opened when a setting is blocked.
+ */
+ @SdkConstant(BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SHOW_ECM_RESTRICTED_SETTING_DIALOG =
+ "android.app.ecm.action.SHOW_ECM_RESTRICTED_SETTING_DIALOG";
+
+ /** A map of ECM states to their corresponding app op states */
+ @Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ECM_STATE_"}, value = {EcmState.ECM_STATE_NOT_GUARDED,
+ EcmState.ECM_STATE_GUARDED, EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED,
+ EcmState.ECM_STATE_IMPLICIT})
+ private @interface EcmState {
+ int ECM_STATE_NOT_GUARDED = AppOpsManager.MODE_ALLOWED;
+ int ECM_STATE_GUARDED = AppOpsManager.MODE_ERRORED;
+ int ECM_STATE_GUARDED_AND_ACKNOWLEDGED = AppOpsManager.MODE_IGNORED;
+ int ECM_STATE_IMPLICIT = AppOpsManager.MODE_DEFAULT;
+ }
+
+ private static final String LOG_TAG = EnhancedConfirmationManager.class.getSimpleName();
+
+ private static final ArraySet<String> PROTECTED_SETTINGS = new ArraySet<>();
+
+ static {
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ // TODO(b/310654015): Add other explicitly protected settings
+ }
+
+ private final @NonNull Context mContext;
+ private final PackageManager mPackageManager;
+
+ private final @NonNull IEnhancedConfirmationManager mService;
+
+ /**
+ * @hide
+ */
+ public EnhancedConfirmationManager(@NonNull Context context,
+ @NonNull IEnhancedConfirmationManager service) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mService = service;
+ }
+
+ /**
+ * Check whether a setting is restricted from an app.
+ *
+ * <p>This is {@code true} when the setting is a protected setting (i.e., a sensitive resource),
+ * and the app is restricted (i.e., considered dangerous), and the user has not yet cleared the
+ * app's restriction status (i.e., by clicking "Allow restricted settings" for this app).
+ *
+ * @param packageName package name of the application to check for
+ * @param settingIdentifier identifier of the resource to check to check for
+ * @return {@code true} if the setting is restricted from the app
+ * @throws NameNotFoundException if the provided package was not found
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+ public boolean isRestricted(@NonNull String packageName, @NonNull String settingIdentifier)
+ throws NameNotFoundException {
+ try {
+ return mService.isRestricted(packageName, settingIdentifier,
+ mContext.getUser().getIdentifier());
+ } catch (IllegalArgumentException e) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Clear an app's restriction status (i.e., un-restrict it).
+ *
+ * <p>After this is called, the app will no longer be restricted from accessing any protected
+ * setting by ECM. This method should be called when the user clicks "Allow restricted settings"
+ * for the app.
+ *
+ * @param packageName package name of the application to remove protection from
+ * @throws NameNotFoundException if the provided package was not found
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+ public void clearRestriction(@NonNull String packageName) throws NameNotFoundException {
+ try {
+ mService.clearRestriction(packageName, mContext.getUser().getIdentifier());
+ } catch (IllegalArgumentException e) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Check whether the provided app is eligible to have its restriction status cleared (i.e., the
+ * app is restricted, and the "Restricted setting" dialog has been presented to the user).
+ *
+ * <p>The Settings UI should use method this to check whether to present the user with the
+ * "Allow restricted settings" button.
+ *
+ * @param packageName package name of the application to check for
+ * @return {@code true} if the settings UI should present the user with the ability to clear
+ * restrictions from the provided app
+ * @throws NameNotFoundException if the provided package was not found
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+ public boolean isClearRestrictionAllowed(@NonNull String packageName)
+ throws NameNotFoundException {
+ try {
+ return mService.isClearRestrictionAllowed(packageName,
+ mContext.getUser().getIdentifier());
+ } catch (IllegalArgumentException e) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Mark the app as eligible to have its restriction status cleared.
+ *
+ * <p>This should be called from the "Restricted setting" dialog (which {@link
+ * #createRestrictedSettingDialogIntent} directs to) upon being presented to the user.
+ *
+ * @param packageName package name of the application which should be considered acknowledged
+ * @throws NameNotFoundException if the provided package was not found
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES)
+ public void setClearRestrictionAllowed(@NonNull String packageName)
+ throws NameNotFoundException {
+ try {
+ mService.setClearRestrictionAllowed(packageName, mContext.getUser().getIdentifier());
+ } catch (IllegalArgumentException e) {
+ throw new NameNotFoundException(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets an intent that will open the "Restricted setting" dialog for the specified package
+ * and setting.
+ *
+ * <p>The "Restricted setting" dialog is a dialog that informs the user that the operation
+ * they've attempted to perform is restricted, and provides them with a link explaining how to
+ * proceed.
+ *
+ * @param packageName package name of the restricted application
+ * @param settingIdentifier identifier of the restricted setting
+ * @throws NameNotFoundException if the provided package was not found
+ */
+ public @NonNull Intent createRestrictedSettingDialogIntent(@NonNull String packageName,
+ @NonNull String settingIdentifier) throws NameNotFoundException {
+ Intent intent = new Intent(ACTION_SHOW_ECM_RESTRICTED_SETTING_DIALOG);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(Intent.EXTRA_UID, getPackageUid(packageName));
+ intent.putExtra(Intent.EXTRA_SUBJECT, settingIdentifier);
+ return intent;
+ }
+
+ private int getPackageUid(String packageName) throws NameNotFoundException {
+ return mPackageManager.getApplicationInfoAsUser(packageName, /* flags */ 0,
+ mContext.getUser()).uid;
+ }
+}
diff --git a/framework-s/java/android/app/ecm/IEnhancedConfirmationManager.aidl b/framework-s/java/android/app/ecm/IEnhancedConfirmationManager.aidl
new file mode 100644
index 000000000..5149daa49
--- /dev/null
+++ b/framework-s/java/android/app/ecm/IEnhancedConfirmationManager.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ecm;
+
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+interface IEnhancedConfirmationManager {
+
+ boolean isRestricted(in String packageName, in String settingIdentifier, int userId);
+
+ void clearRestriction(in String packageName, int userId);
+
+ boolean isClearRestrictionAllowed(in String packageName, int userId);
+
+ void setClearRestrictionAllowed(in String packageName, int userId);
+}
diff --git a/framework-s/java/android/app/role/IRoleController.aidl b/framework-s/java/android/app/role/IRoleController.aidl
index 8a43d7fa9..948915f8d 100644
--- a/framework-s/java/android/app/role/IRoleController.aidl
+++ b/framework-s/java/android/app/role/IRoleController.aidl
@@ -40,4 +40,6 @@ oneway interface IRoleController {
in RemoteCallback callback);
void isRoleVisible(in String roleName, in RemoteCallback callback);
+
+ void getLegacyFallbackDisabledRoles(in RemoteCallback callback);
}
diff --git a/framework-s/java/android/app/role/IRoleManager.aidl b/framework-s/java/android/app/role/IRoleManager.aidl
index 5f7cb1bf5..522967630 100644
--- a/framework-s/java/android/app/role/IRoleManager.aidl
+++ b/framework-s/java/android/app/role/IRoleManager.aidl
@@ -25,9 +25,9 @@ import android.os.RemoteCallback;
*/
interface IRoleManager {
- boolean isRoleAvailable(in String roleName);
+ boolean isRoleAvailableAsUser(in String roleName, int userId);
- boolean isRoleHeld(in String roleName, in String packageName);
+ boolean isRoleHeldAsUser(in String roleName, in String packageName, int userId);
List<String> getRoleHoldersAsUser(in String roleName, int userId);
@@ -54,17 +54,30 @@ interface IRoleManager {
void setBypassingRoleQualification(boolean bypassRoleQualification);
- void setRoleNamesFromController(in List<String> roleNames);
+ boolean isRoleFallbackEnabledAsUser(in String roleName, int userId);
- boolean addRoleHolderFromController(in String roleName, in String packageName);
+ void setRoleFallbackEnabledAsUser(in String roleName, boolean fallbackEnabled, int userId);
- boolean removeRoleHolderFromController(in String roleName, in String packageName);
+ void setRoleNamesFromControllerAsUser(in List<String> roleNames, int userId);
- List<String> getHeldRolesFromController(in String packageName);
+ boolean addRoleHolderFromControllerAsUser(in String roleName, in String packageName,
+ int userId);
+
+ boolean removeRoleHolderFromControllerAsUser(in String roleName, in String packageName,
+ int userId);
+
+ List<String> getHeldRolesFromControllerAsUser(in String packageName, int userId);
String getBrowserRoleHolder(int userId);
boolean setBrowserRoleHolder(String packageName, int userId);
String getSmsRoleHolder(int userId);
+
+ String getEmergencyRoleHolder(int userId);
+
+ boolean isRoleVisibleAsUser(in String roleName, int userId);
+
+ boolean isApplicationVisibleForRoleAsUser(in String roleName, in String packageName,
+ int userId);
}
diff --git a/framework-s/java/android/app/role/RoleControllerManager.java b/framework-s/java/android/app/role/RoleControllerManager.java
index 3b990b315..72228c7ca 100644
--- a/framework-s/java/android/app/role/RoleControllerManager.java
+++ b/framework-s/java/android/app/role/RoleControllerManager.java
@@ -37,6 +37,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
+import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -48,6 +49,17 @@ import java.util.function.Consumer;
*/
public class RoleControllerManager {
+ /**
+ * Bundle key for the payload of RoleController APIs
+ */
+ public static final String KEY_RESULT = RoleControllerManager.class.getName() + ".key.RESULT";
+
+ /**
+ * Bundle key for the error of RoleController APIs
+ */
+ public static final String KEY_EXCEPTION = RoleControllerManager.class.getName()
+ + ".key.EXCEPTION";
+
private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;
@@ -138,9 +150,9 @@ public class RoleControllerManager {
*/
public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.grantDefaultRoles(new RemoteCallback(future::complete));
+ AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Boolean> future = new AndroidFuture<>();
+ service.grantDefaultRoles(createBooleanRemoteCallback(future));
return future;
});
propagateCallback(operation, "grantDefaultRoles", executor, callback);
@@ -153,10 +165,10 @@ public class RoleControllerManager {
*/
public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
+ AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Boolean> future = new AndroidFuture<>();
service.onAddRoleHolder(roleName, packageName, flags,
- new RemoteCallback(future::complete));
+ createBooleanRemoteCallback(future));
return future;
});
propagateCallback(operation, "onAddRoleHolder", callback);
@@ -169,10 +181,10 @@ public class RoleControllerManager {
*/
public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
+ AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Boolean> future = new AndroidFuture<>();
service.onRemoveRoleHolder(roleName, packageName, flags,
- new RemoteCallback(future::complete));
+ createBooleanRemoteCallback(future));
return future;
});
propagateCallback(operation, "onRemoveRoleHolder", callback);
@@ -185,10 +197,9 @@ public class RoleControllerManager {
*/
public void onClearRoleHolders(@NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.onClearRoleHolders(roleName, flags,
- new RemoteCallback(future::complete));
+ AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Boolean> future = new AndroidFuture<>();
+ service.onClearRoleHolders(roleName, flags, createBooleanRemoteCallback(future));
return future;
});
propagateCallback(operation, "onClearRoleHolders", callback);
@@ -202,10 +213,10 @@ public class RoleControllerManager {
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
+ AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Boolean> future = new AndroidFuture<>();
service.isApplicationVisibleForRole(roleName, packageName,
- new RemoteCallback(future::complete));
+ createBooleanRemoteCallback(future));
return future;
});
propagateCallback(operation, "isApplicationVisibleForRole", executor, callback);
@@ -219,15 +230,62 @@ public class RoleControllerManager {
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
public void isRoleVisible(@NonNull String roleName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.isRoleVisible(roleName, new RemoteCallback(future::complete));
+ AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> {
+ AndroidFuture<Boolean> future = new AndroidFuture<>();
+ service.isRoleVisible(roleName, createBooleanRemoteCallback(future));
return future;
});
propagateCallback(operation, "isRoleVisible", executor, callback);
}
- private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
+ /**
+ * @see RoleControllerService#onGrantDefaultRoles()
+ *
+ * @hide
+ */
+ public void getLegacyFallbackDisabledRoles(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<List<String>> callback) {
+ mRemoteService.postAsync(service -> {
+ AndroidFuture<List<String>> future = new AndroidFuture<>();
+ service.getLegacyFallbackDisabledRoles(new RemoteCallback(result -> {
+ Exception exception = (Exception) result.getSerializable(KEY_EXCEPTION);
+ if (exception != null) {
+ future.completeExceptionally(exception);
+ } else {
+ future.complete(result.getStringArrayList(KEY_RESULT));
+ }
+ }));
+ return future;
+ }).orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ .whenComplete((res, err) -> executor.execute(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (err != null) {
+ Log.e(LOG_TAG, "Error calling getLegacyFallbackDisabledRoles()",
+ err);
+ callback.accept(null);
+ } else {
+ callback.accept(res);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }));
+ }
+
+ @NonNull
+ private RemoteCallback createBooleanRemoteCallback(@NonNull AndroidFuture<Boolean> future) {
+ return new RemoteCallback(result -> {
+ Exception exception = (Exception) result.getSerializable(KEY_EXCEPTION);
+ if (exception != null) {
+ future.completeExceptionally(exception);
+ } else {
+ future.complete(result.getBoolean(KEY_RESULT));
+ }
+ });
+ }
+
+ private void propagateCallback(AndroidFuture<Boolean> operation, String opName,
@CallbackExecutor @NonNull Executor executor,
Consumer<Boolean> destination) {
operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
@@ -238,7 +296,7 @@ public class RoleControllerManager {
Log.e(LOG_TAG, "Error calling " + opName + "()", err);
destination.accept(false);
} else {
- destination.accept(res != null);
+ destination.accept(res);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -246,7 +304,7 @@ public class RoleControllerManager {
}));
}
- private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
+ private void propagateCallback(AndroidFuture<Boolean> operation, String opName,
RemoteCallback destination) {
operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
.whenComplete((res, err) -> {
@@ -256,7 +314,7 @@ public class RoleControllerManager {
Log.e(LOG_TAG, "Error calling " + opName + "()", err);
destination.sendResult(null);
} else {
- destination.sendResult(res);
+ destination.sendResult(res ? Bundle.EMPTY : null);
}
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/framework-s/java/android/app/role/RoleControllerService.java b/framework-s/java/android/app/role/RoleControllerService.java
index cf7872913..2155ee4eb 100644
--- a/framework-s/java/android/app/role/RoleControllerService.java
+++ b/framework-s/java/android/app/role/RoleControllerService.java
@@ -17,6 +17,7 @@
package android.app.role;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -31,9 +32,13 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import android.permission.flags.Flags;
+import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -52,6 +57,7 @@ import java.util.concurrent.Executor;
@Deprecated
@SystemApi
public abstract class RoleControllerService extends Service {
+ private static final String LOG_TAG = RoleControllerService.class.getSimpleName();
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -85,7 +91,6 @@ public abstract class RoleControllerService extends Service {
@Override
public void grantDefaultRoles(RemoteCallback callback) {
enforceCallerSystemUid("grantDefaultRoles");
-
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.post(() -> RoleControllerService.this.grantDefaultRoles(callback));
@@ -95,10 +100,6 @@ public abstract class RoleControllerService extends Service {
public void onAddRoleHolder(String roleName, String packageName, int flags,
RemoteCallback callback) {
enforceCallerSystemUid("onAddRoleHolder");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.post(() -> RoleControllerService.this.onAddRoleHolder(roleName,
@@ -109,10 +110,6 @@ public abstract class RoleControllerService extends Service {
public void onRemoveRoleHolder(String roleName, String packageName, int flags,
RemoteCallback callback) {
enforceCallerSystemUid("onRemoveRoleHolder");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.post(() -> RoleControllerService.this.onRemoveRoleHolder(roleName,
@@ -122,8 +119,6 @@ public abstract class RoleControllerService extends Service {
@Override
public void onClearRoleHolders(String roleName, int flags, RemoteCallback callback) {
enforceCallerSystemUid("onClearRoleHolders");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.post(() -> RoleControllerService.this.onClearRoleHolders(roleName,
@@ -141,64 +136,127 @@ public abstract class RoleControllerService extends Service {
public void isApplicationQualifiedForRole(String roleName, String packageName,
RemoteCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
- boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName);
- callback.sendResult(qualified ? Bundle.EMPTY : null);
+ Bundle result = new Bundle();
+ try {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkStringNotEmpty(packageName,
+ "packageName cannot be null or empty");
+ boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName);
+ result.putBoolean(RoleControllerManager.KEY_RESULT, qualified);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
@Override
public void isApplicationVisibleForRole(String roleName, String packageName,
RemoteCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
- boolean visible = onIsApplicationVisibleForRole(roleName, packageName);
- callback.sendResult(visible ? Bundle.EMPTY : null);
+ Bundle result = new Bundle();
+ try {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkStringNotEmpty(packageName,
+ "packageName cannot be null or empty");
+ boolean visible = onIsApplicationVisibleForRole(roleName, packageName);
+ result.putBoolean(RoleControllerManager.KEY_RESULT, visible);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
@Override
public void isRoleVisible(String roleName, RemoteCallback callback) {
enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
+ Objects.requireNonNull(callback, "callback cannot be null");
+
+ Bundle result = new Bundle();
+ try {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ boolean visible = onIsRoleVisible(roleName);
+ result.putBoolean(RoleControllerManager.KEY_RESULT, visible);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
+ }
+
+ @Override
+ public void getLegacyFallbackDisabledRoles(RemoteCallback callback) {
+ enforceCallerSystemUid("getLegacyFallbackDisabledRoles");
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
- boolean visible = onIsRoleVisible(roleName);
- callback.sendResult(visible ? Bundle.EMPTY : null);
+ Bundle result = new Bundle();
+ try {
+ List<String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles();
+ result.putStringArrayList(RoleControllerManager.KEY_RESULT,
+ new ArrayList<>(legacyFallbackDisabledRoles));
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
};
}
- private void grantDefaultRoles(@NonNull RemoteCallback callback) {
- boolean successful = onGrantDefaultRoles();
- callback.sendResult(successful ? Bundle.EMPTY : null);
+ private void grantDefaultRoles(RemoteCallback callback) {
+ Bundle result = new Bundle();
+ try {
+ boolean successful = onGrantDefaultRoles();
+ result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- boolean successful = onAddRoleHolder(roleName, packageName, flags);
- callback.sendResult(successful ? Bundle.EMPTY : null);
+ @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) {
+ Bundle result = new Bundle();
+ try {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkStringNotEmpty(packageName,
+ "packageName cannot be null or empty");
+ boolean successful = onAddRoleHolder(roleName, packageName, flags);
+ result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
private void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- boolean successful = onRemoveRoleHolder(roleName, packageName, flags);
- callback.sendResult(successful ? Bundle.EMPTY : null);
+ @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) {
+ Bundle result = new Bundle();
+ try {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkStringNotEmpty(packageName,
+ "packageName cannot be null or empty");
+ boolean successful = onRemoveRoleHolder(roleName, packageName, flags);
+ result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
private void onClearRoleHolders(@NonNull String roleName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- boolean successful = onClearRoleHolders(roleName, flags);
- callback.sendResult(successful ? Bundle.EMPTY : null);
+ @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) {
+ Bundle result = new Bundle();
+ try {
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ boolean successful = onClearRoleHolders(roleName, flags);
+ result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
+ } catch (Exception e) {
+ result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
+ }
+ callback.sendResult(result);
}
/**
@@ -301,4 +359,17 @@ public abstract class RoleControllerService extends Service {
* @return whether the role should be visible to user
*/
public abstract boolean onIsRoleVisible(@NonNull String roleName);
+
+ /**
+ * Get the legacy fallback disabled state.
+ *
+ * @return A list of role names with disabled fallback state.
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
+ @NonNull
+ public List<String> onGetLegacyFallbackDisabledRoles() {
+ Log.wtf(LOG_TAG, "onGetLegacyFallbackDisabledRoles is unsupported by this version of"
+ + " PermissionController");
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/framework-s/java/android/app/role/RoleManager.java b/framework-s/java/android/app/role/RoleManager.java
index de697f801..4b8c9b388 100644
--- a/framework-s/java/android/app/role/RoleManager.java
+++ b/framework-s/java/android/app/role/RoleManager.java
@@ -18,6 +18,7 @@ package android.app.role;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,9 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -35,6 +39,7 @@ import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.util.ArrayMap;
import android.util.SparseArray;
@@ -42,7 +47,10 @@ import androidx.annotation.RequiresApi;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.SdkLevel;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -138,6 +146,15 @@ public final class RoleManager {
public static final String ROLE_NOTES = "android.app.role.NOTES";
/**
+ * The name of the Wallet role.
+ *
+ * @see android.nfc.cardemulation.CardEmulation
+ */
+ @FlaggedApi(Flags.FLAG_WALLET_ROLE_ENABLED)
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final String ROLE_WALLET = "android.app.role.WALLET";
+
+ /**
* The name of the system wellbeing role.
*
* @hide
@@ -197,6 +214,7 @@ public final class RoleManager {
* @hide
*/
@IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP })
+ @Retention(RetentionPolicy.SOURCE)
public @interface ManageHoldersFlags {}
/**
@@ -210,6 +228,17 @@ public final class RoleManager {
public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1;
/**
+ * For apps targeting Android V and above, several methods are now user-handle-aware, which
+ * means they use the user contained within the context. For apps targeting an SDK version
+ * <em>below</em> this, the user of the calling process will be used.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long ROLE_MANAGER_USER_HANDLE_AWARE = 303742236L;
+
+ /**
* The action used to request user approval of a role for an application.
*
* @hide
@@ -284,10 +313,12 @@ public final class RoleManager {
*
* @return whether the role is available in the system
*/
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ UserHandle user = getContextUserIfAppropriate();
try {
- return mService.isRoleAvailable(roleName);
+ return mService.isRoleAvailableAsUser(roleName, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -300,10 +331,13 @@ public final class RoleManager {
*
* @return whether the calling application is holding the role
*/
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean isRoleHeld(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ UserHandle user = getContextUserIfAppropriate();
try {
- return mService.isRoleHeld(roleName, mContext.getPackageName());
+ return mService.isRoleHeldAsUser(roleName, mContext.getPackageName(),
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -326,8 +360,9 @@ public final class RoleManager {
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public List<String> getRoleHolders(@NonNull String roleName) {
- return getRoleHoldersAsUser(roleName, Process.myUserHandle());
+ return getRoleHoldersAsUser(roleName, getContextUserIfAppropriate());
}
/**
@@ -475,10 +510,10 @@ public final class RoleManager {
}
/**
- * Get package names of the applications holding the role for a default application.
+ * Get package name of the application holding the role for a default application.
* <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_DEFAULT_APPLICATIONS}.
+ * Only roles describing default applications can be used with this method. They can have
+ * at most one holder.
*
* @param roleName the name of the default application role to get
*
@@ -506,8 +541,8 @@ public final class RoleManager {
/**
* Set a specific application as the default application.
* <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_DEFAULT_APPLICATIONS}.
+ * Only roles describing default applications can be used with this method. They can have
+ * at most one holder.
*
* @param roleName the name of the default application role to set the role holder for
* @param packageName the package name of the application to set as the default application,
@@ -527,8 +562,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 {
@@ -690,6 +725,53 @@ public final class RoleManager {
}
/**
+ * Check whether role currently enables fallback to default holder.
+ * <p>
+ * This is based on the "None" holder being actively selected, in which case don't fallback.
+ *
+ * @param roleName the name of the role being queried
+ *
+ * @return whether fallback is enabled for the provided role
+ *
+ * @hide
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
+ @UserHandleAware
+ @SystemApi
+ public boolean isRoleFallbackEnabled(@NonNull String roleName) {
+ try {
+ return mService.isRoleFallbackEnabledAsUser(roleName,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether role should fallback to a default role holder.
+ *
+ * @param roleName the name of the role being queried.
+ * @param fallbackEnabled whether to enable fallback holders for this role.
+ *
+ * @hide
+ */
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
+ @UserHandleAware
+ @SystemApi
+ public void setRoleFallbackEnabled(@NonNull String roleName, boolean fallbackEnabled) {
+ try {
+ mService.setRoleFallbackEnabledAsUser(roleName, fallbackEnabled,
+ mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the names of all the available roles. Should only be called from
* {@link android.app.role.RoleControllerService}.
* <p>
@@ -706,10 +788,12 @@ public final class RoleManager {
@Deprecated
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public void setRoleNamesFromController(@NonNull List<String> roleNames) {
Objects.requireNonNull(roleNames, "roleNames cannot be null");
+ UserHandle user = getContextUserIfAppropriate();
try {
- mService.setRoleNamesFromController(roleNames);
+ mService.setRoleNamesFromControllerAsUser(roleNames, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -740,12 +824,15 @@ public final class RoleManager {
@Deprecated
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean addRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+ UserHandle user = getContextUserIfAppropriate();
try {
- return mService.addRoleHolderFromController(roleName, packageName);
+ return mService.addRoleHolderFromControllerAsUser(roleName, packageName,
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -776,12 +863,15 @@ public final class RoleManager {
@Deprecated
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean removeRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+ UserHandle user = getContextUserIfAppropriate();
try {
- return mService.removeRoleHolderFromController(roleName, packageName);
+ return mService.removeRoleHolderFromControllerAsUser(roleName, packageName,
+ user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -802,15 +892,22 @@ public final class RoleManager {
@NonNull
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public List<String> getHeldRolesFromController(@NonNull String packageName) {
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+ UserHandle user = getContextUserIfAppropriate();
try {
- return mService.getHeldRolesFromController(packageName);
+ return mService.getHeldRolesFromControllerAsUser(packageName, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ private UserHandle getContextUserIfAppropriate() {
+ return CompatChanges.isChangeEnabled(ROLE_MANAGER_USER_HANDLE_AWARE) ? mContext.getUser()
+ : Process.myUserHandle();
+ }
+
/**
* Get the role holder of {@link #ROLE_BROWSER} without requiring
* {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
@@ -878,6 +975,28 @@ public final class RoleManager {
}
/**
+ * Allows getting the role holder for {@link #ROLE_EMERGENCY} without requiring
+ * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}.
+ *
+ * @param userId the user ID to get the default emergency package for
+ * @return the package name of the default emergency app, or {@code null} if none
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED)
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @Nullable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public String getEmergencyRoleHolder(@UserIdInt int userId) {
+ try {
+ return mService.getEmergencyRoleHolder(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check whether a role should be visible to user.
*
* @param roleName name of the role to check for
@@ -888,10 +1007,29 @@ public final class RoleManager {
*/
@RequiresApi(Build.VERSION_CODES.S)
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
@SystemApi
public void isRoleVisible(@NonNull String roleName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- getRoleControllerManager().isRoleVisible(roleName, executor, callback);
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ int userId = getContextUserIfAppropriate().getIdentifier();
+ boolean visible;
+ try {
+ visible = mService.isRoleVisibleAsUser(roleName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ executor.execute(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ callback.accept(visible);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ } else {
+ getRoleControllerManager().isRoleVisible(roleName, executor, callback);
+ }
}
/**
@@ -910,11 +1048,30 @@ public final class RoleManager {
*/
@RequiresApi(Build.VERSION_CODES.S)
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
@SystemApi
public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- getRoleControllerManager().isApplicationVisibleForRole(roleName, packageName, executor,
- callback);
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ int userId = getContextUserIfAppropriate().getIdentifier();
+ boolean visible;
+ try {
+ visible = mService.isApplicationVisibleForRoleAsUser(roleName, packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ executor.execute(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ callback.accept(visible);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ } else {
+ getRoleControllerManager().isApplicationVisibleForRole(roleName, packageName, executor,
+ callback);
+ }
}
@NonNull
diff --git a/framework-s/java/android/app/role/TEST_MAPPING b/framework-s/java/android/app/role/TEST_MAPPING
index ce53dca05..46b148e68 100644
--- a/framework-s/java/android/app/role/TEST_MAPPING
+++ b/framework-s/java/android/app/role/TEST_MAPPING
@@ -25,5 +25,41 @@
}
]
}
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsRoleTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ }
+ ]
+ }
]
}
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..ff0c66e24 100644
--- a/framework-s/java/android/safetycenter/config/SafetySource.java
+++ b/framework-s/java/android/safetycenter/config/SafetySource.java
@@ -18,9 +18,11 @@ package android.safetycenter.config;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM;
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,6 +36,7 @@ import android.util.ArraySet;
import androidx.annotation.RequiresApi;
import com.android.modules.utils.build.SdkLevel;
+import com.android.permission.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -182,6 +185,9 @@ public final class SafetySource implements Parcelable {
builder.addPackageCertificateHash(certs.get(i));
}
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ builder.setTitleForPrivateProfileResId(in.readInt());
+ }
return builder.build();
}
@@ -207,6 +213,7 @@ public final class SafetySource implements Parcelable {
private final boolean mNotificationsAllowed;
@Nullable final String mDeduplicationGroup;
@NonNull private final Set<String> mPackageCertificateHashes;
+ @StringRes private final int mTitleForPrivateProfileResId;
private SafetySource(
@SafetySourceType int type,
@@ -224,7 +231,8 @@ public final class SafetySource implements Parcelable {
boolean refreshOnPageOpenAllowed,
boolean notificationsAllowed,
@Nullable String deduplicationGroup,
- @NonNull Set<String> packageCertificateHashes) {
+ @NonNull Set<String> packageCertificateHashes,
+ @StringRes int titleForPrivateProfileResId) {
mType = type;
mId = id;
mPackageName = packageName;
@@ -241,6 +249,7 @@ public final class SafetySource implements Parcelable {
mNotificationsAllowed = notificationsAllowed;
mDeduplicationGroup = deduplicationGroup;
mPackageCertificateHashes = Set.copyOf(packageCertificateHashes);
+ mTitleForPrivateProfileResId = titleForPrivateProfileResId;
}
/** Returns the type of this safety source. */
@@ -297,7 +306,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;
}
@@ -346,6 +356,37 @@ public final class SafetySource implements Parcelable {
}
/**
+ * Returns the resource id of the title for private profile of this safety source.
+ *
+ * <p>The id refers to a string resource that is either accessible from any resource context or
+ * that is accessible from the same resource context that was used to load the Safety Center
+ * configuration. The id is {@link Resources#ID_NULL} when a title for private profile is not
+ * provided.
+ *
+ * @throws UnsupportedOperationException if the source is of type {@link
+ * SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} or if the profile property of the source is
+ * set to {@link SafetySource#PROFILE_PRIMARY}
+ */
+ @FlaggedApi(Flags.FLAG_PRIVATE_PROFILE_TITLE_API)
+ @RequiresApi(VANILLA_ICE_CREAM)
+ @StringRes
+ public int getTitleForPrivateProfileResId() {
+ if (!SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException(
+ "getTitleForPrivateProfileResId unsupported for SDKs lower than V");
+ }
+ if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
+ throw new UnsupportedOperationException(
+ "getTitleForPrivateProfileResId unsupported for issue-only safety source");
+ }
+ if (mProfile == PROFILE_PRIMARY) {
+ throw new UnsupportedOperationException(
+ "getTitleForPrivateProfileResId unsupported for primary profile safety source");
+ }
+ return mTitleForPrivateProfileResId;
+ }
+
+ /**
* Returns the resource id of the summary of this safety source.
*
* <p>The id refers to a string resource that is either accessible from any resource context or
@@ -488,7 +529,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 +545,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 +568,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;
}
@@ -550,7 +594,8 @@ public final class SafetySource implements Parcelable {
&& mRefreshOnPageOpenAllowed == that.mRefreshOnPageOpenAllowed
&& mNotificationsAllowed == that.mNotificationsAllowed
&& Objects.equals(mDeduplicationGroup, that.mDeduplicationGroup)
- && Objects.equals(mPackageCertificateHashes, that.mPackageCertificateHashes);
+ && Objects.equals(mPackageCertificateHashes, that.mPackageCertificateHashes)
+ && mTitleForPrivateProfileResId == that.mTitleForPrivateProfileResId;
}
@Override
@@ -571,7 +616,8 @@ public final class SafetySource implements Parcelable {
mRefreshOnPageOpenAllowed,
mNotificationsAllowed,
mDeduplicationGroup,
- mPackageCertificateHashes);
+ mPackageCertificateHashes,
+ mTitleForPrivateProfileResId);
}
@Override
@@ -609,6 +655,8 @@ public final class SafetySource implements Parcelable {
+ mDeduplicationGroup
+ ", mPackageCertificateHashes="
+ mPackageCertificateHashes
+ + ", mTitleForPrivateProfileResId="
+ + mTitleForPrivateProfileResId
+ '}';
}
@@ -637,6 +685,9 @@ public final class SafetySource implements Parcelable {
dest.writeString(mDeduplicationGroup);
dest.writeStringList(List.copyOf(mPackageCertificateHashes));
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ dest.writeInt(mTitleForPrivateProfileResId);
+ }
}
/** Builder class for {@link SafetySource}. */
@@ -658,6 +709,7 @@ public final class SafetySource implements Parcelable {
@Nullable private Boolean mNotificationsAllowed;
@Nullable private String mDeduplicationGroup;
@NonNull private final ArraySet<String> mPackageCertificateHashes = new ArraySet<>();
+ @Nullable @StringRes private Integer mTitleForPrivateProfileResId;
/** Creates a {@link Builder} for a {@link SafetySource}. */
public Builder(@SafetySourceType int type) {
@@ -668,7 +720,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;
@@ -687,6 +740,7 @@ public final class SafetySource implements Parcelable {
mNotificationsAllowed = safetySource.mNotificationsAllowed;
mDeduplicationGroup = safetySource.mDeduplicationGroup;
mPackageCertificateHashes.addAll(safetySource.mPackageCertificateHashes);
+ mTitleForPrivateProfileResId = safetySource.mTitleForPrivateProfileResId;
}
/**
@@ -754,6 +808,32 @@ public final class SafetySource implements Parcelable {
}
/**
+ * Sets the resource id of the title for private profile of this safety source.
+ *
+ * <p>The id must refer to a string resource that is either accessible from any resource
+ * context or that is accessible from the same resource context that was used to load the
+ * Safety Center configuration. The id defaults to {@link Resources#ID_NULL} when a title
+ * for private profile is not provided.
+ *
+ * <p>The title for private profile is required if the profile property of the source is set
+ * to {@link SafetySource#PROFILE_ALL} and either the source is of type static or the source
+ * is a source of type dynamic that is not hidden and that does not provide search terms.
+ * The title for private profile is prohibited for sources of type issue-only and if the
+ * profile property of the source is not set to {@link SafetySource#PROFILE_ALL}.
+ */
+ @FlaggedApi(Flags.FLAG_PRIVATE_PROFILE_TITLE_API)
+ @RequiresApi(VANILLA_ICE_CREAM)
+ @NonNull
+ public Builder setTitleForPrivateProfileResId(@StringRes int titleForPrivateProfileResId) {
+ if (!SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException(
+ "setTitleForPrivateProfileResId unsupported for SDKs lower than V");
+ }
+ mTitleForPrivateProfileResId = titleForPrivateProfileResId;
+ return this;
+ }
+
+ /**
* Sets the resource id of the summary of this safety source.
*
* <p>The id must refer to a string resource that is either accessible from any resource
@@ -884,7 +964,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 +984,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 +1001,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;
@@ -976,7 +1059,7 @@ public final class SafetySource implements Parcelable {
PROFILE_NONE,
PROFILE_PRIMARY,
PROFILE_ALL);
- boolean hasWork = profile == PROFILE_ALL;
+ boolean hasAllProfiles = profile == PROFILE_ALL;
int searchTermsResId =
BuilderUtils.validateResId(
@@ -992,8 +1075,8 @@ public final class SafetySource implements Parcelable {
BuilderUtils.validateResId(
mTitleForWorkResId,
"titleForWork",
- hasWork && titleRequired,
- !hasWork || isIssueOnly);
+ hasAllProfiles && titleRequired,
+ !hasAllProfiles || isIssueOnly);
int summaryResId =
BuilderUtils.validateResId(
@@ -1044,6 +1127,16 @@ public final class SafetySource implements Parcelable {
packageCertificateHashes, "packageCertificateHashes", false, isStatic);
}
+ int titleForPrivateProfileResId = Resources.ID_NULL;
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ titleForPrivateProfileResId =
+ BuilderUtils.validateResId(
+ mTitleForPrivateProfileResId,
+ "titleForPrivateProfile",
+ hasAllProfiles && titleRequired,
+ !hasAllProfiles || isIssueOnly);
+ }
+
return new SafetySource(
type,
id,
@@ -1060,7 +1153,8 @@ public final class SafetySource implements Parcelable {
refreshOnPageOpenAllowed,
notificationsAllowed,
deduplicationGroup,
- packageCertificateHashes);
+ packageCertificateHashes,
+ titleForPrivateProfileResId);
}
}
}
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/framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd b/framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd
new file mode 100644
index 000000000..20b1e7655
--- /dev/null
+++ b/framework-s/java/android/safetycenter/config/safety_center_config-v35.xsd
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<!-- This file contains comments that define constraints that cannot be covered by the XSD language -->
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ version="1.0">
+
+ <xsd:element name="safety-center-config" type="safety-center-config"/>
+
+ <xsd:complexType name="safety-center-config">
+ <xsd:sequence>
+ <xsd:element name="safety-sources-config" type="safety-sources-config"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="safety-sources-config">
+ <xsd:sequence>
+ <xsd:element
+ name="safety-sources-group" type="safety-sources-group"
+ minOccurs="1" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="safety-sources-group">
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element name="dynamic-safety-source" type="dynamic-safety-source"/>
+ <xsd:element name="static-safety-source" type="static-safety-source"/>
+ <xsd:element name="issue-only-safety-source" type="issue-only-safety-source"/>
+ </xsd:choice>
+ <!-- id must be unique among safety sources groups -->
+ <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
+ <!-- title is required unless the group contains issue only and/or internal sources -->
+ <xsd:attribute name="title" type="runtimeStringResourceName"/>
+ <xsd:attribute name="summary" type="runtimeStringResourceName"/>
+ <xsd:attribute name="statelessIconType" type="statelessIconTypeOrStringResourceName"
+ default="none"/>
+ <!-- type is inferred from other attributes and the group content if omitted -->
+ <xsd:attribute name="type" type="groupTypeOrStringResourceName"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="dynamic-safety-source">
+ <!-- id must be unique among safety sources -->
+ <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
+ <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/>
+ <!-- optional comma-separated set of certificate hashes, if provided will be used for validation. -->
+ <xsd:attribute name="packageCertificateHashes" type="stringOrStringResourceName"/>
+ <!-- title is required if initialDisplayState is not set to hidden or if searchTerms are provided -->
+ <xsd:attribute name="title" type="runtimeStringResourceName"/>
+ <!-- titleForWork is required if profile is set to all_profiles, and initialDisplayState is not set to hidden or if searchTerms are provided -->
+ <!-- titleForWork is prohibited if profile is set to primary_profile_only -->
+ <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/>
+ <!-- titleForPrivateProfile is required if profile is set to all_profiles, and initialDisplayState is not set to hidden or if searchTerms are provided -->
+ <!-- titleForPrivateProfile is prohibited if profile is set to primary_profile_only -->
+ <xsd:attribute name="titleForPrivateProfile" type="runtimeStringResourceName"/>
+ <!-- summary is required if initialDisplayState is not set to hidden -->
+ <xsd:attribute name="summary" type="runtimeStringResourceName"/>
+ <!-- intentAction is required if initialDisplayState is set to enabled -->
+ <xsd:attribute name="intentAction" type="stringOrStringResourceName"/>
+ <xsd:attribute name="profile" type="profile" use="required"/>
+ <xsd:attribute name="initialDisplayState" type="initialDisplayStateOrStringResourceName"
+ default="enabled"/>
+ <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/>
+ <xsd:attribute name="searchTerms" type="runtimeStringResourceName"/>
+ <xsd:attribute name="loggingAllowed" type="booleanOrStringResourceName" default="true"/>
+ <xsd:attribute name="refreshOnPageOpenAllowed" type="booleanOrStringResourceName"
+ default="false"/>
+ <xsd:attribute name="notificationsAllowed" type="booleanOrStringResourceName"
+ default="false"/>
+ <xsd:attribute name="deduplicationGroup" type="stringOrStringResourceName"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="issue-only-safety-source">
+ <!-- id must be unique among safety sources -->
+ <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
+ <xsd:attribute name="packageName" type="stringOrStringResourceName" use="required"/>
+ <!-- optional comma-separated set of certificate hashes, if provided will be used for validation. -->
+ <xsd:attribute name="packageCertificateHashes" type="stringOrStringResourceName"/>
+ <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/>
+ <xsd:attribute name="maxSeverityLevel" type="intOrStringResourceName" default="2147483647"/>
+ <xsd:attribute name="loggingAllowed" type="booleanOrStringResourceName" default="true"/>
+ <xsd:attribute name="refreshOnPageOpenAllowed" type="booleanOrStringResourceName"
+ default="false"/>
+ <xsd:attribute name="notificationsAllowed" type="booleanOrStringResourceName"
+ default="false"/>
+ <xsd:attribute name="deduplicationGroup" type="stringOrStringResourceName"/>
+ </xsd:complexType>
+
+ <xsd:complexType name="static-safety-source">
+ <!-- id must be unique among safety sources -->
+ <xsd:attribute name="id" type="idOrStringResourceName" use="required"/>
+ <xsd:attribute name="packageName" type="stringOrStringResourceName"/>
+ <xsd:attribute name="title" type="runtimeStringResourceName" use="required"/>
+ <!-- titleForWork is required if profile is set to all_profiles -->
+ <!-- titleForWork is prohibited if profile is set to primary_profile_only -->
+ <xsd:attribute name="titleForWork" type="runtimeStringResourceName"/>
+ <!-- titleForPrivateProfile is required if profile is set to all_profiles -->
+ <!-- titleForPrivateProfile is prohibited if profile is set to primary_profile_only -->
+ <xsd:attribute name="titleForPrivateProfile" type="runtimeStringResourceName"/>
+ <xsd:attribute name="summary" type="runtimeStringResourceName"/>
+ <xsd:attribute name="intentAction" type="stringOrStringResourceName" use="required"/>
+ <xsd:attribute name="profile" type="profileOrStringResourceName" use="required"/>
+ <xsd:attribute name="searchTerms" type="runtimeStringResourceName"/>
+ </xsd:complexType>
+
+ <xsd:simpleType name="intOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:int. -->
+ <xsd:union memberTypes="stringResourceName xsd:int"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="booleanOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:boolean. -->
+ <xsd:union memberTypes="stringResourceName xsd:boolean"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="stringOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:string. -->
+ <xsd:union memberTypes="stringResourceName xsd:string"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="idOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type xsd:string. -->
+ <xsd:union memberTypes="stringResourceName id"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="id">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="[0-9a-zA-Z_-]+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="statelessIconTypeOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type statelessIconType. -->
+ <xsd:union memberTypes="stringResourceName statelessIconType"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="statelessIconType">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="none"/>
+ <xsd:enumeration value="privacy"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="profileOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type profile. -->
+ <xsd:union memberTypes="stringResourceName profile"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="profile">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="primary_profile_only"/>
+ <xsd:enumeration value="all_profiles"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="initialDisplayStateOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type initialDisplayState. -->
+ <xsd:union memberTypes="stringResourceName initialDisplayState"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="initialDisplayState">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="enabled"/>
+ <xsd:enumeration value="disabled"/>
+ <xsd:enumeration value="hidden"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="groupTypeOrStringResourceName">
+ <!-- String resource names will be resolved only once at parse time. -->
+ <!-- Locale changes and device config changes will be ignored. -->
+ <!-- The value of the string resource must be of type groupType. -->
+ <xsd:union memberTypes="stringResourceName groupType"/>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="groupType">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="stateless"/>
+ <xsd:enumeration value="stateful"/>
+ <xsd:enumeration value="hidden"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="runtimeStringResourceName">
+ <!-- String resource names will be resolved at runtime whenever the string value is used. -->
+ <xsd:union memberTypes="stringResourceName"/>
+ </xsd:simpleType>
+
+ <!-- String resource names will be ignored for any attribute not directly or indirectly marked as stringResourceName. -->
+ <!-- A stringResourceName is a fully qualified resource name of the form "@package:string/entry". Package is required. -->
+ <xsd:simpleType name="stringResourceName">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="@([a-z]+\.)*[a-z]+:string/.+"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+</xsd:schema>
diff --git a/framework-s/lint-baseline-framework-permission-s.xml b/framework-s/lint-baseline-framework-permission-s.xml
new file mode 100644
index 000000000..053c4ed43
--- /dev/null
+++ b/framework-s/lint-baseline-framework-permission-s.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha08" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha08">
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onGetLegacyFallbackDisabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `getLegacyFallbackDisabledRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" List&lt;String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/framework-s/java/android/app/role/RoleControllerService.java"
+ line="197"
+ column="64"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `onGetLegacyFallbackDisabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `getLegacyFallbackDisabledRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" List&lt;String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/framework-s/java/android/app/role/RoleControllerService.java"
+ line="189"
+ column="60"/>
+ </issue>
+
+</issues>
diff --git a/framework-s/lint-baseline.xml b/framework-s/lint-baseline.xml
index b91b959e4..eb4ed1796 100644
--- a/framework-s/lint-baseline.xml
+++ b/framework-s/lint-baseline.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.Context#getUser`"
- errorLine1=" .append(String.valueOf(mContext.getUser().getIdentifier())).println();"
- errorLine2=" ~~~~~~~">
+ message="Call requires API level 33 (current min is 30): `android.util.Slog#e`"
+ errorLine1=' Slog.e(LOG_TAG, "Failed to unbind: " + e);'
+ errorLine2=" ~">
<location
file="frameworks/base/core/java/com/android/internal/infra/ServiceConnector.java"
- line="707"
- column="53"/>
+ line="576"
+ column="26"/>
</issue>
<issue
@@ -25,13 +25,13 @@
<issue
id="NewApi"
- message="Call requires API level 33 (current min is 30): `android.util.Slog#e`"
- errorLine1=' Slog.e(LOG_TAG, "Failed to unbind: " + e);'
- errorLine2=" ~">
+ message="Call requires API level 31 (current min is 30): `android.content.Context#getUser`"
+ errorLine1=" .append(String.valueOf(mContext.getUser().getIdentifier())).println();"
+ errorLine2=" ~~~~~~~">
<location
file="frameworks/base/core/java/com/android/internal/infra/ServiceConnector.java"
- line="576"
- column="26"/>
+ line="707"
+ column="53"/>
</issue>
</issues> \ No newline at end of file
diff --git a/framework/Android.bp b/framework/Android.bp
index 644534e6a..bd4c9dc81 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -55,12 +56,3 @@ java_sdk_library {
hostdex: true,
installable: true,
}
-
-java_api_contribution {
- name: "framework-permission-public-stubs",
- api_surface: "public",
- api_file: "api/current.txt",
- visibility: [
- "//build/orchestrator/apis",
- ],
-}
diff --git a/framework/java/android/permission/PermissionState.java b/framework/java/android/permission/PermissionStateUnused.java
index e810db8ec..21b21cc27 100644
--- a/framework/java/android/permission/PermissionState.java
+++ b/framework/java/android/permission/PermissionStateUnused.java
@@ -19,4 +19,4 @@ package android.permission;
/**
* @hide
*/
-public class PermissionState {}
+public class PermissionStateUnused {}
diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt
deleted file mode 100644
index 40d8f07d9..000000000
--- a/ktfmt_includes.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-+SafetyCenter
-+tests
diff --git a/permissions/Android.bp b/permissions/Android.bp
index 8c7dab40b..fb84e0537 100644
--- a/permissions/Android.bp
+++ b/permissions/Android.bp
@@ -1,4 +1,3 @@
-
//
// Copyright (C) 2022 The Android Open Source Project
//
@@ -15,13 +14,12 @@
// limitations under the License.
//
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
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/permissions/com.android.permissioncontroller.xml b/permissions/com.android.permissioncontroller.xml
index 957a29ee0..60484e24e 100644
--- a/permissions/com.android.permissioncontroller.xml
+++ b/permissions/com.android.permissioncontroller.xml
@@ -34,5 +34,6 @@
<permission name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" />
<permission name="android.permission.START_TASKS_FROM_RECENTS" />
<permission name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
+ <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
</privapp-permissions>
</permissions>
diff --git a/service/Android.bp b/service/Android.bp
index 96b8fbb96..d80fa4f0d 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -100,6 +101,7 @@ java_sdk_library {
"modules-utils-backgroundthread",
"modules-utils-build",
"modules-utils-os",
+ "role-controller",
"safety-center-config",
"safety-center-internal-data",
"safety-center-pending-intents",
@@ -107,7 +109,9 @@ java_sdk_library {
"safety-center-resources-lib",
"service-permission-shared",
"service-permission-statsd",
+ "permissioncontroller-statsd",
"service-permission-proto-stream",
+ "com.android.permission.flags-aconfig-java",
],
errorprone: {
javacflags: ["-Xep:GuardedBy:ERROR"],
@@ -122,7 +126,7 @@ java_sdk_library {
"-Xno-receiver-assertions",
],
lint: {
- strict_updatability_linting: true,
+ baseline_filename: "lint-baseline.xml",
},
min_sdk_version: "30",
sdk_version: "system_server_current",
@@ -133,18 +137,27 @@ java_sdk_library {
installable: true,
permitted_packages: [
"com.android.access",
+ "com.android.ecm",
"com.android.permission",
"com.android.role",
"com.android.safetycenter",
],
+ optimize: {
+ proguard_compatibility: false, // TODO(b/215530220): remove when this is default behavior
+ proguard_flags_files: ["proguard.flags"],
+ },
+ aconfig_declarations: [
+ "android.permission.flags-aconfig",
+ "com.android.permission.flags-aconfig",
+ ],
}
genrule {
name: "statslog-service-permission-java-gen",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --java $(out) --module permissioncontroller" +
- " --javaPackage com.android.permission" +
- " --javaClass PermissionStatsLog --minApiLevel 29",
+ " --javaPackage com.android.permission" +
+ " --javaClass PermissionStatsLog --minApiLevel 29",
out: ["com/android/permission/PermissionStatsLog.java"],
}
@@ -154,6 +167,7 @@ java_library {
":statslog-service-permission-java-gen",
],
libs: [
+ "androidx.annotation_annotation",
"framework-statsd.stubs.module_lib",
],
apex_available: [
diff --git a/service/api/system-server-current.txt b/service/api/system-server-current.txt
index b1869c2c7..30fbab484 100644
--- a/service/api/system-server-current.txt
+++ b/service/api/system-server-current.txt
@@ -45,6 +45,8 @@ package com.android.role.persistence {
public final class RolesState {
ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
+ ctor @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>, @NonNull java.util.Set<java.lang.String>);
+ method @FlaggedApi("android.permission.flags.system_server_role_controller_enabled") @NonNull public java.util.Set<java.lang.String> getFallbackEnabledRoles();
method @Nullable public String getPackagesHash();
method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
method public int getVersion();
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index 2b8765cf5..4e7eb1d9a 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -1,7 +1,19 @@
+rule android.os.*FeatureFlags* com.android.permission.jarjar.@0
+rule android.os.FeatureFlags* com.android.permission.jarjar.@0
+rule android.os.FeatureFlags com.android.permission.jarjar.@0
+rule android.os.Flags com.android.permission.jarjar.@0
rule android.os.HandlerExecutor com.android.permission.jarjar.@0
+rule android.permission.flags.*FeatureFlags* com.android.permission.jarjar.@0
+rule android.permission.flags.FeatureFlags* com.android.permission.jarjar.@0
+rule android.permission.flags.FeatureFlags com.android.permission.jarjar.@0
+rule android.permission.flags.Flags com.android.permission.jarjar.@0
rule android.util.IndentingPrintWriter com.android.permission.jarjar.@0
rule com.android.internal.** com.android.permission.jarjar.@0
rule com.android.modules.** com.android.permission.jarjar.@0
+rule com.android.permission.flags.*FeatureFlags* com.android.permission.jarjar.@0
+rule com.android.permission.flags.FeatureFlags* com.android.permission.jarjar.@0
+rule com.android.permission.flags.FeatureFlags com.android.permission.jarjar.@0
+rule com.android.permission.flags.Flags com.android.permission.jarjar.@0
rule com.android.role.*Proto com.android.permission.jarjar.@0
# TODO(b/236200992): Revisit addition of rule com.android.safetycenter.annotations,
# com.android.safetycenter.internaldata, com.android.safetycenter.pendingintents and
@@ -12,3 +24,4 @@ rule com.android.safetycenter.pendingintents.** com.android.permission.jarjar.@0
rule com.android.safetycenter.resources.** com.android.permission.jarjar.@0
rule com.google.protobuf.** com.android.permission.jarjar.@0
rule kotlin.** com.android.permission.jarjar.@0
+rule com.android.permissioncontroller.PermissionControllerStatsLog com.android.permission.jarjar.@0
diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java
new file mode 100644
index 000000000..c6672f2ae
--- /dev/null
+++ b/service/java/com/android/ecm/EnhancedConfirmationService.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ecm;
+
+import android.Manifest;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.app.ecm.EnhancedConfirmationManager;
+import android.app.ecm.IEnhancedConfirmationManager;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.SignedPackage;
+import android.os.Binder;
+import android.os.Build;
+import android.os.SystemConfigManager;
+import android.os.UserHandle;
+import android.permission.flags.Flags;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import com.android.internal.util.Preconditions;
+import com.android.permission.util.UserUtils;
+import com.android.server.SystemService;
+
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Service for ECM (Enhanced Confirmation Mode).
+ *
+ * @see EnhancedConfirmationManager
+ *
+ * @hide
+ */
+@Keep
+@FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+public class EnhancedConfirmationService extends SystemService {
+ private static final String LOG_TAG = EnhancedConfirmationService.class.getSimpleName();
+
+ private Map<String, List<byte[]>> mTrustedPackageCertDigests;
+ private Map<String, List<byte[]>> mTrustedInstallerCertDigests;
+
+ public EnhancedConfirmationService(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ Context context = getContext();
+ SystemConfigManager systemConfigManager = context.getSystemService(
+ SystemConfigManager.class);
+ mTrustedPackageCertDigests = toTrustedPackageMap(
+ systemConfigManager.getEnhancedConfirmationTrustedPackages());
+ mTrustedInstallerCertDigests = toTrustedPackageMap(
+ systemConfigManager.getEnhancedConfirmationTrustedInstallers());
+
+ publishBinderService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE, new Stub());
+ }
+
+ private Map<String, List<byte[]>> toTrustedPackageMap(Set<SignedPackage> signedPackages) {
+ ArrayMap<String, List<byte[]>> trustedPackageMap = new ArrayMap<>();
+ for (SignedPackage signedPackage : signedPackages) {
+ ArrayList<byte[]> certDigests = (ArrayList<byte[]>) trustedPackageMap.computeIfAbsent(
+ signedPackage.getPackageName(), packageName -> new ArrayList<>(1));
+ certDigests.add(signedPackage.getCertificateDigest());
+ }
+ return trustedPackageMap;
+ }
+
+ private class Stub extends IEnhancedConfirmationManager.Stub {
+
+ /** A map of ECM states to their corresponding app op states */
+ @Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"ECM_STATE_"}, value = {EcmState.ECM_STATE_NOT_GUARDED,
+ EcmState.ECM_STATE_GUARDED, EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED,
+ EcmState.ECM_STATE_IMPLICIT})
+ private @interface EcmState {
+ int ECM_STATE_NOT_GUARDED = AppOpsManager.MODE_ALLOWED;
+ int ECM_STATE_GUARDED = AppOpsManager.MODE_ERRORED;
+ int ECM_STATE_GUARDED_AND_ACKNOWLEDGED = AppOpsManager.MODE_IGNORED;
+ int ECM_STATE_IMPLICIT = AppOpsManager.MODE_DEFAULT;
+ }
+
+ private static final ArraySet<String> PROTECTED_SETTINGS = new ArraySet<>();
+
+ static {
+ // Runtime permissions
+ // TODO(b/310654818): Construct this list by permission group instead of by permission
+ PROTECTED_SETTINGS.add(Manifest.permission.READ_PHONE_STATE);
+ PROTECTED_SETTINGS.add(Manifest.permission.READ_PHONE_NUMBERS);
+ PROTECTED_SETTINGS.add(Manifest.permission.CALL_PHONE);
+ PROTECTED_SETTINGS.add(Manifest.permission.ADD_VOICEMAIL);
+ PROTECTED_SETTINGS.add(Manifest.permission.USE_SIP);
+ PROTECTED_SETTINGS.add(Manifest.permission.ANSWER_PHONE_CALLS);
+ PROTECTED_SETTINGS.add(Manifest.permission.ACCEPT_HANDOVER);
+ PROTECTED_SETTINGS.add(Manifest.permission_group.PHONE);
+
+ PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS);
+ PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_SMS);
+ PROTECTED_SETTINGS.add(Manifest.permission.READ_SMS);
+ PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_MMS);
+ PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_WAP_PUSH);
+ PROTECTED_SETTINGS.add(Manifest.permission.READ_CELL_BROADCASTS);
+ PROTECTED_SETTINGS.add(Manifest.permission_group.SMS);
+
+ PROTECTED_SETTINGS.add(Manifest.permission.BIND_DEVICE_ADMIN);
+ // TODO(b/310654818): Add other explicitly protected runtime permissions
+ // App ops
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_GET_USAGE_STATS);
+ PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
+ // Default application roles.
+ PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER);
+ PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS);
+ // TODO(b/310654015): Add other explicitly protected settings
+ }
+
+ private final @NonNull Context mContext;
+ private final String mAttributionTag;
+ private final AppOpsManager mAppOpsManager;
+ private final PackageManager mPackageManager;
+
+ Stub() {
+ Context context = getContext();
+ mContext = context;
+ mAttributionTag = context.getAttributionTag();
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mPackageManager = context.getPackageManager();
+ }
+
+ public boolean isRestricted(@NonNull String packageName, @NonNull String settingIdentifier,
+ @UserIdInt int userId) {
+ enforcePermissions("isRestricted", userId);
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
+ Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+ Preconditions.checkStringNotEmpty(settingIdentifier,
+ "settingIdentifier cannot be null or empty");
+
+ try {
+ return isSettingEcmProtected(settingIdentifier) && isPackageEcmGuarded(packageName,
+ userId);
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public void clearRestriction(@NonNull String packageName, @UserIdInt int userId) {
+ enforcePermissions("clearRestriction", userId);
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ return;
+ }
+
+ Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+
+ try {
+ int state = getAppEcmState(packageName, userId);
+ boolean isAllowed = state == EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED;
+ if (!isAllowed) {
+ throw new IllegalStateException("Clear restriction attempted but not allowed");
+ }
+ setAppEcmState(packageName, EcmState.ECM_STATE_NOT_GUARDED, userId);
+ EnhancedConfirmationStatsLogUtils.INSTANCE.logRestrictionCleared(
+ getPackageUid(packageName, userId));
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public boolean isClearRestrictionAllowed(@NonNull String packageName,
+ @UserIdInt int userId) {
+ enforcePermissions("isClearRestrictionAllowed", userId);
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ return false;
+ }
+
+ Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+
+ try {
+ int state = getAppEcmState(packageName, userId);
+ return state == EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED;
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public void setClearRestrictionAllowed(@NonNull String packageName, @UserIdInt int userId) {
+ enforcePermissions("setClearRestrictionAllowed", userId);
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ return;
+ }
+
+ Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+
+ try {
+ if (isPackageEcmGuarded(packageName, userId)) {
+ setAppEcmState(packageName, EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED,
+ userId);
+ }
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private void enforcePermissions(@NonNull String methodName, @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, methodName, mContext);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_ENHANCED_CONFIRMATION_STATES, methodName);
+ }
+
+ private boolean isPackageEcmGuarded(@NonNull String packageName, @UserIdInt int userId)
+ throws NameNotFoundException {
+ ApplicationInfo applicationInfo = getApplicationInfoAsUser(packageName, userId);
+ // Always trust allow-listed and pre-installed packages
+ if (isAllowlistedPackage(packageName) || isAllowlistedInstaller(packageName)
+ || isPackagePreinstalled(applicationInfo)) {
+ return false;
+ }
+
+ // If the package already has an explicitly-set state, use that
+ @EcmState int ecmState = getAppEcmState(packageName, userId);
+ if (ecmState == EcmState.ECM_STATE_GUARDED
+ || ecmState == EcmState.ECM_STATE_GUARDED_AND_ACKNOWLEDGED) {
+ return true;
+ }
+ if (ecmState == EcmState.ECM_STATE_NOT_GUARDED) {
+ return false;
+ }
+
+ // Otherwise, lazily decide whether the app is considered guarded.
+ InstallSourceInfo installSource;
+ try {
+ installSource = mContext.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getInstallSourceInfo(packageName);
+ } catch (NameNotFoundException e) {
+ Log.w(LOG_TAG, "Package not found: " + packageName);
+ return false;
+ }
+
+ // These install sources are always considered dangerous.
+ // PackageInstallers that are trusted can use these as a signal that the
+ // packages they've installed aren't as trusted as themselves.
+ int packageSource = installSource.getPackageSource();
+ if (packageSource == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+ || packageSource == PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
+ return true;
+ }
+ String installingPackageName = installSource.getInstallingPackageName();
+ ApplicationInfo installingApplicationInfo =
+ getApplicationInfoAsUser(installingPackageName, userId);
+
+ // ECM doesn't consider a transitive chain of trust for install sources.
+ // If this package hasn't been explicitly handled by this point
+ // then it is exempt from ECM if the immediate parent is a trusted installer
+ return !(trustPackagesInstalledViaNonAllowlistedInstallers()
+ || isPackagePreinstalled(installingApplicationInfo)
+ || isAllowlistedInstaller(installingPackageName));
+ }
+
+ private boolean isAllowlistedPackage(String packageName) {
+ return isPackageSignedWithAnyOf(packageName,
+ mTrustedPackageCertDigests.get(packageName));
+ }
+
+ private boolean isAllowlistedInstaller(String packageName) {
+ return isPackageSignedWithAnyOf(packageName,
+ mTrustedInstallerCertDigests.get(packageName));
+ }
+
+ private boolean isPackageSignedWithAnyOf(String packageName, List<byte[]> certDigests) {
+ if (packageName != null && certDigests != null) {
+ for (int i = 0, count = certDigests.size(); i < count; i++) {
+ byte[] trustedCertDigest = certDigests.get(i);
+ if (mPackageManager.hasSigningCertificate(packageName, trustedCertDigest,
+ PackageManager.CERT_INPUT_SHA256)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return {@code true} if zero {@code <enhanced-confirmation-trusted-installer>} entries
+ * are defined in {@code frameworks/base/data/etc/enhanced-confirmation.xml}; in this case,
+ * we treat all installers as trusted.
+ */
+ private boolean trustPackagesInstalledViaNonAllowlistedInstallers() {
+ return mTrustedInstallerCertDigests.isEmpty();
+ }
+
+ private boolean isPackagePreinstalled(@Nullable ApplicationInfo applicationInfo) {
+ if (applicationInfo == null) {
+ return false;
+ }
+ return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ private void setAppEcmState(@NonNull String packageName, @EcmState int ecmState,
+ @UserIdInt int userId) throws NameNotFoundException {
+ int packageUid = getPackageUid(packageName, userId);
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ mAppOpsManager.setMode(AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, packageUid,
+ packageName, ecmState);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private @EcmState int getAppEcmState(@NonNull String packageName, @UserIdInt int userId)
+ throws NameNotFoundException {
+ int packageUid = getPackageUid(packageName, userId);
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS,
+ packageUid, packageName, mAttributionTag, /* message */ null);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private boolean isSettingEcmProtected(@NonNull String settingIdentifier) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ || mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ return false;
+ }
+
+ if (PROTECTED_SETTINGS.contains(settingIdentifier)) {
+ return true;
+ }
+ // TODO(b/310218979): Add role selections as protected settings
+ return false;
+ }
+
+ @Nullable
+ private ApplicationInfo getApplicationInfoAsUser(@Nullable String packageName,
+ @UserIdInt int userId) {
+ if (packageName == null) {
+ Log.w(LOG_TAG, "The packageName should not be null.");
+ return null;
+ }
+ try {
+ return mPackageManager.getApplicationInfoAsUser(packageName, /* flags */ 0,
+ UserHandle.of(userId));
+ } catch (NameNotFoundException e) {
+ Log.w(LOG_TAG, "Package not found: " + packageName, e);
+ return null;
+ }
+ }
+
+ private int getPackageUid(@NonNull String packageName, @UserIdInt int userId)
+ throws NameNotFoundException {
+ return mPackageManager.getApplicationInfoAsUser(packageName, /* flags */ 0,
+ UserHandle.of(userId)).uid;
+ }
+ }
+}
diff --git a/service/java/com/android/ecm/EnhancedConfirmationStatsLogUtils.kt b/service/java/com/android/ecm/EnhancedConfirmationStatsLogUtils.kt
new file mode 100644
index 000000000..5bf925fc7
--- /dev/null
+++ b/service/java/com/android/ecm/EnhancedConfirmationStatsLogUtils.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ecm
+
+import android.permission.flags.Flags
+import android.util.Log
+import com.android.internal.annotations.Keep
+import com.android.modules.utils.build.SdkLevel
+import com.android.permissioncontroller.PermissionControllerStatsLog
+
+/**
+ * Provides ECM-related metrics logging for Permission APEX services.
+ *
+ * @hide
+ */
+@Keep
+object EnhancedConfirmationStatsLogUtils {
+ private val LOG_TAG = EnhancedConfirmationStatsLogUtils::class.java.simpleName
+
+ fun logRestrictionCleared(uid: Int) {
+ if (!SdkLevel.isAtLeastV() || !Flags.enhancedConfirmationModeApisEnabled()) {
+ return
+ }
+ Log.v(LOG_TAG, "ENHANCED_CONFIRMATION_RESTRICTION_CLEARED: uid='$uid'")
+
+ PermissionControllerStatsLog.write(
+ PermissionControllerStatsLog.ENHANCED_CONFIRMATION_RESTRICTION_CLEARED,
+ uid
+ )
+ }
+}
diff --git a/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index f3ba5aaef..4c8eb1df1 100644
--- a/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -214,9 +214,6 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers
@Override
public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
@NonNull UserHandle user) {
- File reserveFile = getReserveCopyFile(user);
- reserveFile.delete();
-
File file = getFile(user);
AtomicFile atomicFile = new AtomicFile(file);
FileOutputStream outputStream = null;
@@ -240,6 +237,7 @@ public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPers
IoUtils.closeQuietly(outputStream);
}
+ File reserveFile = getReserveCopyFile(user);
try (FileInputStream in = new FileInputStream(file);
FileOutputStream out = new FileOutputStream(reserveFile)) {
FileUtils.copy(in, out);
diff --git a/service/java/com/android/permission/util/UserUtils.java b/service/java/com/android/permission/util/UserUtils.java
index 8205be239..33389a88f 100644
--- a/service/java/com/android/permission/util/UserUtils.java
+++ b/service/java/com/android/permission/util/UserUtils.java
@@ -25,7 +25,9 @@ import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.util.Preconditions;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permission.compat.UserHandleCompat;
+import com.android.permission.flags.Flags;
import java.util.List;
@@ -92,6 +94,39 @@ public final class UserUtils {
}
/**
+ * Returns whether the given {@code userId} is a private profile. Note that private profiles are
+ * allowed from Android V+ only, so this method will return false on Sdk levels below that.
+ */
+ public static boolean isPrivateProfile(@UserIdInt int userId, @NonNull Context context) {
+ if (!isPrivateProfileSupported()) {
+ return false;
+ }
+ // It's needed to clear the calling identity because we are going to query the UserManager
+ // for isPrivateProfile() and Context for createContextAsUser, which requires one of the
+ // following permissions:
+ // MANAGE_USERS, QUERY_USERS, or INTERACT_ACROSS_USERS.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Context userContext = context
+ .createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+ UserManager userManager = userContext.getSystemService(UserManager.class);
+ return userManager != null && userManager.isPrivateProfile();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+
+ /**
+ * Returns whether private profile's allowed to exist. This can be true iff the SdkLevel is at
+ * least V AND the permission module's private profile feature flag is enabled.
+ */
+ public static boolean isPrivateProfileSupported() {
+ //TODO(b/286539356) add the os feature flag protection when available.
+ return SdkLevel.isAtLeastV() && Flags.privateProfileSupported();
+ }
+
+ /**
* Returns whether a given {@code userId} corresponds to a running managed profile, i.e. the
* user is running and the quiet mode is not enabled.
*/
diff --git a/service/java/com/android/role/LocalRoleController.java b/service/java/com/android/role/LocalRoleController.java
new file mode 100644
index 000000000..03508d1cb
--- /dev/null
+++ b/service/java/com/android/role/LocalRoleController.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import android.annotation.CallbackExecutor;
+import android.app.role.RoleControllerService;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+
+import com.android.role.controller.service.RoleControllerServiceImpl;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+public class LocalRoleController implements RoleController {
+
+ @NonNull
+ private final RoleControllerServiceImpl mService;
+ @NonNull
+ private final HandlerThread mWorkerThread;
+ @NonNull
+ private final Handler mWorkerHandler;
+
+ public LocalRoleController(@NonNull UserHandle user, @NonNull Context context) {
+ mService = new RoleControllerServiceImpl(user, context);
+ mWorkerThread = new HandlerThread(RoleControllerService.class.getSimpleName());
+ mWorkerThread.start();
+ mWorkerHandler = new Handler(mWorkerThread.getLooper());
+ }
+
+ @Override
+ public void grantDefaultRoles(@NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
+ mWorkerHandler.post(() -> {
+ boolean successful = mService.onGrantDefaultRoles();
+ executor.execute(() -> callback.accept(successful));
+ });
+ }
+
+ @Override
+ public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
+ mWorkerHandler.post(() -> {
+ boolean successful = mService.onAddRoleHolder(roleName, packageName, flags);
+ callback.sendResult(successful ? Bundle.EMPTY : null);
+ });
+ }
+
+ @Override
+ public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
+ mWorkerHandler.post(() -> {
+ boolean successful = mService.onRemoveRoleHolder(roleName, packageName, flags);
+ callback.sendResult(successful ? Bundle.EMPTY : null);
+ });
+ }
+
+ @Override
+ public void onClearRoleHolders(@NonNull String roleName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
+ mWorkerHandler.post(() -> {
+ boolean successful = mService.onClearRoleHolders(roleName, flags);
+ callback.sendResult(successful ? Bundle.EMPTY : null);
+ });
+ }
+
+ @Override
+ public boolean isRoleVisible(@NonNull String roleName) {
+ return mService.onIsRoleVisible(roleName);
+ }
+
+ @Override
+ public boolean isApplicationVisibleForRole(@NonNull String roleName,
+ @NonNull String packageName) {
+ return mService.onIsApplicationVisibleForRole(roleName, packageName);
+ }
+
+ @Override
+ public void getLegacyFallbackDisabledRoles(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<List<String>> callback) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/service/java/com/android/role/RemoteRoleController.java b/service/java/com/android/role/RemoteRoleController.java
new file mode 100644
index 000000000..0920dd56a
--- /dev/null
+++ b/service/java/com/android/role/RemoteRoleController.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import android.annotation.CallbackExecutor;
+import android.app.role.RoleControllerManager;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.RemoteCallback;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+
+import com.android.permission.util.ForegroundThread;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+public class RemoteRoleController implements RoleController {
+ @NonNull
+ private final RoleControllerManager mRoleControllerManager;
+
+ public RemoteRoleController(@NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = context.createContextAsUser(user, 0);
+ mRoleControllerManager =
+ RoleControllerManager.createWithInitializedRemoteServiceComponentName(
+ ForegroundThread.getHandler(), userContext);
+ }
+
+ @Override
+ public void grantDefaultRoles(@NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
+ mRoleControllerManager.grantDefaultRoles(executor, callback);
+ }
+
+ @Override
+ public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
+ mRoleControllerManager.onAddRoleHolder(roleName, packageName, flags, callback);
+ }
+
+ @Override
+ public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
+ mRoleControllerManager.onRemoveRoleHolder(roleName, packageName, flags, callback);
+ }
+
+ @Override
+ public void onClearRoleHolders(@NonNull String roleName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
+ mRoleControllerManager.onClearRoleHolders(roleName, flags, callback);
+ }
+
+ @Override
+ public boolean isRoleVisible(@NonNull String roleName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isApplicationVisibleForRole(@NonNull String roleName,
+ @NonNull String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void getLegacyFallbackDisabledRoles(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<List<String>> callback) {
+ mRoleControllerManager.getLegacyFallbackDisabledRoles(executor, callback);
+ }
+}
diff --git a/service/java/com/android/role/RoleController.java b/service/java/com/android/role/RoleController.java
new file mode 100644
index 000000000..6ebbdfd45
--- /dev/null
+++ b/service/java/com/android/role/RoleController.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.app.role.RoleManager;
+import android.os.RemoteCallback;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+public interface RoleController {
+ /**
+ * @see android.app.role.RoleControllerManager#grantDefaultRoles
+ */
+ void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback);
+
+ /**
+ * @see android.app.role.RoleControllerManager#onAddRoleHolder
+ */
+ void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback);
+
+ /**
+ * @see android.app.role.RoleControllerManager#onRemoveRoleHolder
+ */
+ void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback);
+
+ /**
+ * @see android.app.role.RoleControllerManager#onClearRoleHolders
+ */
+ void onClearRoleHolders(@NonNull String roleName,
+ @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback);
+
+ /**
+ * @see android.app.role.RoleControllerManager#isRoleVisible
+ */
+ boolean isRoleVisible(@NonNull String roleName);
+
+ /**
+ * @see android.app.role.RoleControllerManager#isApplicationVisibleForRole
+ */
+ boolean isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName);
+
+ /**
+ * @see android.app.role.RoleControllerManager#getLegacyFallbackDisabledRoles
+ */
+ void getLegacyFallbackDisabledRoles(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<List<String>> callback);
+}
diff --git a/service/java/com/android/role/RoleService.java b/service/java/com/android/role/RoleService.java
index 485be4e72..8348d4064 100644
--- a/service/java/com/android/role/RoleService.java
+++ b/service/java/com/android/role/RoleService.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManager;
import android.app.role.RoleControllerManager;
@@ -33,6 +34,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -41,6 +44,9 @@ import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.permission.flags.Flags;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -55,6 +61,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.modules.utils.build.SdkLevel;
import com.android.permission.compat.UserHandleCompat;
import com.android.permission.util.ArrayUtils;
import com.android.permission.util.CollectionUtils;
@@ -93,15 +100,22 @@ public class RoleService extends SystemService implements RoleUserState.Callback
private static final long GRANT_DEFAULT_ROLES_INTERVAL_MILLIS = 1000;
- private static final String[] DEFAULT_APPLICATION_ROLES = {
- RoleManager.ROLE_ASSISTANT,
- RoleManager.ROLE_BROWSER,
- RoleManager.ROLE_CALL_REDIRECTION,
- RoleManager.ROLE_CALL_SCREENING,
- RoleManager.ROLE_DIALER,
- RoleManager.ROLE_HOME,
- RoleManager.ROLE_SMS,
- };
+ private static final String[] DEFAULT_APPLICATION_ROLES;
+
+ static {
+ List<String> defaultApplicationRoles = new ArrayList<>();
+ defaultApplicationRoles.add(RoleManager.ROLE_ASSISTANT);
+ defaultApplicationRoles.add(RoleManager.ROLE_BROWSER);
+ defaultApplicationRoles.add(RoleManager.ROLE_CALL_REDIRECTION);
+ defaultApplicationRoles.add(RoleManager.ROLE_CALL_SCREENING);
+ defaultApplicationRoles.add(RoleManager.ROLE_DIALER);
+ defaultApplicationRoles.add(RoleManager.ROLE_HOME);
+ defaultApplicationRoles.add(RoleManager.ROLE_SMS);
+ if (SdkLevel.isAtLeastV()) {
+ defaultApplicationRoles.add(RoleManager.ROLE_WALLET);
+ }
+ DEFAULT_APPLICATION_ROLES = defaultApplicationRoles.toArray(new String[0]);
+ }
@NonNull
private final AppOpsManager mAppOpsManager;
@@ -124,7 +138,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback
*/
@GuardedBy("mLock")
@NonNull
- private final SparseArray<RoleControllerManager> mControllers = new SparseArray<>();
+ private final SparseArray<RoleController> mControllers = new SparseArray<>();
/**
* Maps user id to its list of listeners.
@@ -181,13 +195,14 @@ public class RoleService extends SystemService implements RoleUserState.Callback
public void onStart() {
publishBinderService(Context.ROLE_SERVICE, new Stub());
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- getContext().registerReceiverForAllUsers(new BroadcastReceiver() {
+ Context context = getContext();
+ IntentFilter packageIntentFilter = new IntentFilter();
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageIntentFilter.addDataScheme("package");
+ packageIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiverForAllUsers(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int userId = UserHandleCompat.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1));
@@ -202,14 +217,82 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
maybeGrantDefaultRolesAsync(userId);
}
- }, intentFilter, null, null);
+ }, packageIntentFilter, null, null);
+
+ if (SdkLevel.isAtLeastV()) {
+ IntentFilter devicePolicyIntentFilter = new IntentFilter();
+ devicePolicyIntentFilter.addAction(
+ DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
+ devicePolicyIntentFilter.addAction(
+ DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
+ devicePolicyIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ context.registerReceiverForAllUsers(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int userId = getSendingUser().getIdentifier();
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Device policy changed (" + intent.getAction()
+ + ") - re-running initial grants for user " + userId);
+ }
+ maybeGrantDefaultRolesAsync(userId);
+ }
+ }, devicePolicyIntentFilter, null, null);
+
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_DEMO_MODE), false,
+ new ContentObserver(ForegroundThread.getHandler()) {
+ public void onChange(boolean selfChange, Uri uri) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Settings.Global.DEVICE_DEMO_MODE changed.");
+ }
+ UserManager userManager =
+ context.getSystemService(UserManager.class);
+ List<UserHandle> users = userManager.getUserHandles(true);
+ int usersSize = users.size();
+ for (int i = 0; i < usersSize; i++) {
+ maybeGrantDefaultRolesAsync(users.get(i).getIdentifier());
+ }
+ }
+ });
+ }
}
@Override
public void onUserStarting(@NonNull TargetUser user) {
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ upgradeLegacyFallbackEnabledRolesIfNeeded(user);
+ }
+
maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier());
}
+ private void upgradeLegacyFallbackEnabledRolesIfNeeded(@NonNull TargetUser user) {
+ int userId = user.getUserHandle().getIdentifier();
+ RoleUserState userState = getOrCreateUserState(userId);
+ if (!userState.isVersionUpgradeNeeded()) {
+ return;
+ }
+ List<String> legacyFallbackDisabledRoles = getLegacyFallbackDisabledRolesSync(userId);
+ if (legacyFallbackDisabledRoles == null) {
+ return;
+ }
+ userState.upgradeVersion(legacyFallbackDisabledRoles);
+ }
+
+ @MainThread
+ private List<String> getLegacyFallbackDisabledRolesSync(@UserIdInt int userId) {
+ AndroidFuture<List<String>> future = new AndroidFuture<>();
+ RoleController controller = new RemoteRoleController(UserHandle.of(userId), getContext());
+ controller.getLegacyFallbackDisabledRoles(ForegroundThread.getExecutor(), future::complete);
+ try {
+ return future.get(30, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ Log.e(LOG_TAG, "Failed to get the legacy role fallback disabled state for user "
+ + userId, e);
+ return null;
+ }
+ }
+
@MainThread
private void maybeGrantDefaultRolesSync(@UserIdInt int userId) {
AndroidFuture<Void> future = maybeGrantDefaultRolesInternal(userId);
@@ -237,6 +320,11 @@ public class RoleService extends SystemService implements RoleUserState.Callback
@AnyThread
@NonNull
private AndroidFuture<Void> maybeGrantDefaultRolesInternal(@UserIdInt int userId) {
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.w(LOG_TAG, "User " + userId + " does not exist");
+ return AndroidFuture.completedFuture(null);
+ }
+
RoleUserState userState = getOrCreateUserState(userId);
String oldPackagesHash = userState.getPackagesHash();
String newPackagesHash = mPlatformHelper.computePackageStateHash(userId);
@@ -277,14 +365,17 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
@NonNull
- private RoleControllerManager getOrCreateController(@UserIdInt int userId) {
+ private RoleController getOrCreateController(@UserIdInt int userId) {
synchronized (mLock) {
- RoleControllerManager controller = mControllers.get(userId);
+ RoleController controller = mControllers.get(userId);
if (controller == null) {
- Context systemContext = getContext();
- Context userContext = systemContext.createContextAsUser(UserHandle.of(userId), 0);
- controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName(
- ForegroundThread.getHandler(), userContext);
+ UserHandle user = UserHandle.of(userId);
+ Context context = getContext();
+ if (SdkLevel.isAtLeastV() && Flags.systemServerRoleControllerEnabled()) {
+ controller = new LocalRoleController(user, context);
+ } else {
+ controller = new RemoteRoleController(user, context);
+ }
mControllers.put(userId, controller);
}
return controller;
@@ -371,22 +462,33 @@ public class RoleService extends SystemService implements RoleUserState.Callback
private class Stub extends IRoleManager.Stub {
@Override
- public boolean isRoleAvailable(@NonNull String roleName) {
+ public boolean isRoleAvailableAsUser(@NonNull String roleName, @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "isRoleAvailableAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- int userId = UserHandleCompat.getUserId(getCallingUid());
return getOrCreateUserState(userId).isRoleAvailable(roleName);
}
@Override
- public boolean isRoleHeld(@NonNull String roleName, @NonNull String packageName) {
- int callingUid = getCallingUid();
- mAppOpsManager.checkPackage(callingUid, packageName);
+ public boolean isRoleHeldAsUser(@NonNull String roleName, @NonNull String packageName,
+ @UserIdInt int userId) {
+ mAppOpsManager.checkPackage(getCallingUid(), packageName);
+
+ UserUtils.enforceCrossUserPermission(userId, false, "isRoleHeldAsUser", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- int userId = UserHandleCompat.getUserId(callingUid);
ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName);
if (roleHolders == null) {
return false;
@@ -434,8 +536,7 @@ public class RoleService extends SystemService implements RoleUserState.Callback
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null");
- getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags,
- callback);
+ getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags, callback);
}
@Override
@@ -522,11 +623,11 @@ public class RoleService extends SystemService implements RoleUserState.Callback
Preconditions.checkArgumentIsSupported(DEFAULT_APPLICATION_ROLES, roleName);
Objects.requireNonNull(callback, "callback cannot be null");
- RoleControllerManager roleControllerManager = getOrCreateController(userId);
+ RoleController roleController = getOrCreateController(userId);
if (packageName != null) {
- roleControllerManager.onAddRoleHolder(roleName, packageName, flags, callback);
+ roleController.onAddRoleHolder(roleName, packageName, flags, callback);
} else {
- roleControllerManager.onClearRoleHolders(roleName, flags, callback);
+ roleController.onClearRoleHolders(roleName, flags, callback);
}
}
@@ -605,54 +706,116 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
@Override
- public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+ public boolean isRoleFallbackEnabledAsUser(@NonNull String roleName,
+ @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "isRoleFallbackEnabledAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "isRoleFallbackEnabledAsUser");
+
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+
+ return getOrCreateUserState(userId).isFallbackEnabled(roleName);
+ }
+
+ @Override
+ public void setRoleFallbackEnabledAsUser(@NonNull String roleName, boolean fallbackEnabled,
+ @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "setRoleFallbackEnabledAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "setRoleFallbackEnabledAsUser");
+
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+
+ getOrCreateUserState(userId).setFallbackEnabled(roleName, fallbackEnabled);
+ }
+
+ @Override
+ public void setRoleNamesFromControllerAsUser(@NonNull List<String> roleNames,
+ @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "setRoleNamesFromControllerAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return;
+ }
+
getContext().enforceCallingOrSelfPermission(
RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "setRoleNamesFromController");
+ "setRoleNamesFromControllerAsUser");
Objects.requireNonNull(roleNames, "roleNames cannot be null");
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
getOrCreateUserState(userId).setRoleNames(roleNames);
}
@Override
- public boolean addRoleHolderFromController(@NonNull String roleName,
- @NonNull String packageName) {
+ public boolean addRoleHolderFromControllerAsUser(@NonNull String roleName,
+ @NonNull String packageName, @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false,
+ "addRoleHolderFromControllerAsUser", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
getContext().enforceCallingOrSelfPermission(
RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "addRoleHolderFromController");
+ "addRoleHolderFromControllerAsUser");
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
return getOrCreateUserState(userId).addRoleHolder(roleName, packageName);
}
@Override
- public boolean removeRoleHolderFromController(@NonNull String roleName,
- @NonNull String packageName) {
+ public boolean removeRoleHolderFromControllerAsUser(@NonNull String roleName,
+ @NonNull String packageName, @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false,
+ "removeRoleHolderFromControllerAsUser", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
getContext().enforceCallingOrSelfPermission(
RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "removeRoleHolderFromController");
+ "removeRoleHolderFromControllerAsUser");
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName);
}
@Override
- public List<String> getHeldRolesFromController(@NonNull String packageName) {
+ public List<String> getHeldRolesFromControllerAsUser(@NonNull String packageName,
+ @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false,
+ "getHeldRolesFromControllerAsUser", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return Collections.emptyList();
+ }
+
getContext().enforceCallingOrSelfPermission(
RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "getRolesHeldFromController");
+ "getHeldRolesFromControllerAsUser");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
return getOrCreateUserState(userId).getHeldRoles(packageName);
}
@@ -772,6 +935,69 @@ public class RoleService extends SystemService implements RoleUserState.Callback
}
@Override
+ public String getEmergencyRoleHolder(int userId) {
+ final Context context = getContext();
+ UserUtils.enforceCrossUserPermission(userId, false, "getEmergencyRoleHolder", context);
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return null;
+ }
+
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE, "getEmergencyRoleHolder");
+
+ final String packageName;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ packageName = CollectionUtils.firstOrNull(getRoleHoldersAsUser(
+ RoleManager.ROLE_EMERGENCY, userId));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ if (packageName != null && !PackageUtils.canCallingOrSelfPackageQuery(packageName,
+ userId, context)) {
+ return null;
+ }
+ return packageName;
+ }
+
+ @Override
+ public boolean isRoleVisibleAsUser(@NonNull String roleName, @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false, "isRoleVisibleAsUser",
+ getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "isRoleVisibleAsUser");
+
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+
+ return getOrCreateController(userId).isRoleVisible(roleName);
+ }
+
+ @Override
+ public boolean isApplicationVisibleForRoleAsUser(@NonNull String roleName,
+ @NonNull String packageName, @UserIdInt int userId) {
+ UserUtils.enforceCrossUserPermission(userId, false,
+ "isApplicationVisibleForRoleAsUser", getContext());
+ if (!UserUtils.isUserExistent(userId, getContext())) {
+ Log.e(LOG_TAG, "user " + userId + " does not exist");
+ return false;
+ }
+
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
+ "isApplicationVisibleForRoleAsUser");
+
+ Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
+ Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
+
+ return getOrCreateController(userId).isApplicationVisibleForRole(roleName, packageName);
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
if (!checkDumpPermission("role", fout)) {
diff --git a/service/java/com/android/role/RoleUserState.java b/service/java/com/android/role/RoleUserState.java
index 727e0f122..974299d03 100644
--- a/service/java/com/android/role/RoleUserState.java
+++ b/service/java/com/android/role/RoleUserState.java
@@ -53,6 +53,8 @@ class RoleUserState {
public static final int VERSION_UNDEFINED = -1;
+ public static final int VERSION_FALLBACK_STATE_MIGRATED = 1;
+
private static final long WRITE_DELAY_MILLIS = 200;
private final RolesPersistence mPersistence = RolesPersistence.createInstance();
@@ -86,6 +88,13 @@ class RoleUserState {
@NonNull
private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
+ /**
+ * Role names of the roles with fallback enabled.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private ArraySet<String> mFallbackEnabledRoles = new ArraySet<>();
+
@GuardedBy("mLock")
private boolean mWriteScheduled;
@@ -141,6 +150,15 @@ class RoleUserState {
}
/**
+ * Checks the version and returns whether a version upgrade is needed.
+ */
+ public boolean isVersionUpgradeNeeded() {
+ synchronized (mLock) {
+ return mVersion < VERSION_FALLBACK_STATE_MIGRATED;
+ }
+ }
+
+ /**
* Get the hash representing the state of packages during the last time initial grants was run.
*
* @return the hash representing the state of packages
@@ -193,6 +211,48 @@ class RoleUserState {
}
}
+ public boolean isFallbackEnabled(@NonNull String roleName) {
+ synchronized (mLock) {
+ return mFallbackEnabledRoles.contains(roleName);
+ }
+ }
+
+ public void setFallbackEnabled(@NonNull String roleName, boolean fallbackEnabled) {
+ synchronized (mLock) {
+ if (!mRoles.containsKey(roleName)) {
+ Log.e(LOG_TAG, "Cannot set fallback enabled for unknown role, role: " + roleName
+ + ", fallbackEnabled: " + fallbackEnabled);
+ return;
+ }
+ if (mFallbackEnabledRoles.contains(roleName) == fallbackEnabled) {
+ return;
+ }
+ if (fallbackEnabled) {
+ mFallbackEnabledRoles.add(roleName);
+ } else {
+ mFallbackEnabledRoles.remove(roleName);
+ }
+ scheduleWriteFileLocked();
+ }
+ }
+
+ /**
+ * Upgrade this user state to the latest version if needed.
+ */
+ public void upgradeVersion(@NonNull List<String> legacyFallbackDisabledRoles) {
+ synchronized (mLock) {
+ if (mVersion < VERSION_FALLBACK_STATE_MIGRATED) {
+ int legacyFallbackDisabledRolesSize = legacyFallbackDisabledRoles.size();
+ for (int i = 0; i < legacyFallbackDisabledRolesSize; i++) {
+ String roleName = legacyFallbackDisabledRoles.get(i);
+ mFallbackEnabledRoles.remove(roleName);
+ }
+ mVersion = VERSION_FALLBACK_STATE_MIGRATED;
+ scheduleWriteFileLocked();
+ }
+ }
+ }
+
/**
* Get whether the role is available.
*
@@ -235,6 +295,7 @@ class RoleUserState {
synchronized (mLock) {
if (!mRoles.containsKey(roleName)) {
mRoles.put(roleName, new ArraySet<>());
+ mFallbackEnabledRoles.add(roleName);
Log.i(LOG_TAG, "Added new role: " + roleName);
scheduleWriteFileLocked();
return true;
@@ -263,6 +324,7 @@ class RoleUserState {
+ " role: " + roleName + ", holders: " + packageNames);
}
mRoles.removeAt(i);
+ mFallbackEnabledRoles.remove(roleName);
changed = true;
}
}
@@ -386,7 +448,8 @@ class RoleUserState {
// Force a reconciliation on next boot if we are bypassing role qualification now.
String packagesHash = mBypassingRoleQualification ? null : mPackagesHash;
roles = new RolesState(mVersion, packagesHash,
- (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked());
+ (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked(),
+ snapshotFallbackEnabledRoles());
}
mPersistence.writeForUser(roles, UserHandle.of(mUserId));
@@ -397,12 +460,15 @@ class RoleUserState {
RolesState roleState = mPersistence.readForUser(UserHandle.of(mUserId));
Map<String, Set<String>> roles;
+ Set<String> fallbackEnabledRoles;
if (roleState != null) {
mVersion = roleState.getVersion();
mPackagesHash = roleState.getPackagesHash();
roles = roleState.getRoles();
+ fallbackEnabledRoles = roleState.getFallbackEnabledRoles();
} else {
roles = mPlatformHelper.getLegacyRoleState(mUserId);
+ fallbackEnabledRoles = roles.keySet();
}
mRoles.clear();
for (Map.Entry<String, Set<String>> entry : roles.entrySet()) {
@@ -410,7 +476,8 @@ class RoleUserState {
ArraySet<String> roleHolders = new ArraySet<>(entry.getValue());
mRoles.put(roleName, roleHolders);
}
-
+ mFallbackEnabledRoles.clear();
+ mFallbackEnabledRoles.addAll(fallbackEnabledRoles);
if (roleState == null) {
scheduleWriteFileLocked();
}
@@ -427,10 +494,12 @@ class RoleUserState {
int version;
String packagesHash;
ArrayMap<String, ArraySet<String>> roles;
+ ArraySet<String> fallbackEnabledRoles;
synchronized (mLock) {
version = mVersion;
packagesHash = mPackagesHash;
roles = snapshotRolesLocked();
+ fallbackEnabledRoles = snapshotFallbackEnabledRoles();
}
long fieldToken = dumpOutputStream.start(fieldName, fieldId);
@@ -442,10 +511,11 @@ class RoleUserState {
for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
String roleName = roles.keyAt(rolesIndex);
ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
+ boolean fallbackEnabled = fallbackEnabledRoles.contains(roleName);
long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
dumpOutputStream.write("name", RoleProto.NAME, roleName);
-
+ dumpOutputStream.write("fallback_enabled", RoleProto.FALLBACK_ENABLED, fallbackEnabled);
int roleHoldersSize = roleHolders.size();
for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
String roleHolder = roleHolders.valueAt(roleHoldersIndex);
@@ -485,6 +555,12 @@ class RoleUserState {
return roles;
}
+ @GuardedBy("mLock")
+ @NonNull
+ private ArraySet<String> snapshotFallbackEnabledRoles() {
+ return new ArraySet<>(mFallbackEnabledRoles);
+ }
+
/**
* Destroy this user state and delete the corresponding file. Any pending writes to the file
* will be cancelled, and any future interaction with this state will throw an exception.
diff --git a/service/java/com/android/role/TEST_MAPPING b/service/java/com/android/role/TEST_MAPPING
index 15173a9da..e0e1160d8 100644
--- a/service/java/com/android/role/TEST_MAPPING
+++ b/service/java/com/android/role/TEST_MAPPING
@@ -33,5 +33,49 @@
}
]
}
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.StatsdAppSecurityAtomTest#testRoleHolder"
+ }
+ ]
+ },
+ {
+ "name": "CtsRoleTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ }
+ ]
+ }
]
}
diff --git a/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/service/java/com/android/role/persistence/RolesPersistenceImpl.java
index 76cf8f81f..f308304b5 100644
--- a/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -66,6 +66,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
private static final String ATTRIBUTE_VERSION = "version";
private static final String ATTRIBUTE_NAME = "name";
+ private static final String ATTRIBUTE_FALLBACK_ENABLED = "fallbackEnabled";
private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
@VisibleForTesting
@@ -142,6 +143,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
String packagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
Map<String, Set<String>> roles = new ArrayMap<>();
+ Set<String> fallbackEnabledRoles = new ArraySet<>();
int type;
int depth;
int innerDepth = parser.getDepth() + 1;
@@ -153,12 +155,16 @@ public class RolesPersistenceImpl implements RolesPersistence {
if (parser.getName().equals(TAG_ROLE)) {
String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
+ String fallbackEnabled = parser.getAttributeValue(null, ATTRIBUTE_FALLBACK_ENABLED);
+ if (Boolean.parseBoolean(fallbackEnabled)) {
+ fallbackEnabledRoles.add(roleName);
+ }
Set<String> roleHolders = parseRoleHolders(parser);
roles.put(roleName, roleHolders);
}
}
- return new RolesState(version, packagesHash, roles);
+ return new RolesState(version, packagesHash, roles, fallbackEnabledRoles);
}
@NonNull
@@ -184,9 +190,6 @@ public class RolesPersistenceImpl implements RolesPersistence {
@Override
public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) {
- File reserveFile = getReserveCopyFile(user);
- reserveFile.delete();
-
File file = getFile(user);
AtomicFile atomicFile = new AtomicFile(file);
FileOutputStream outputStream = null;
@@ -211,6 +214,7 @@ public class RolesPersistenceImpl implements RolesPersistence {
IoUtils.closeQuietly(outputStream);
}
+ File reserveFile = getReserveCopyFile(user);
try (FileInputStream in = new FileInputStream(file);
FileOutputStream out = new FileOutputStream(reserveFile)) {
FileUtils.copy(in, out);
@@ -238,12 +242,16 @@ public class RolesPersistenceImpl implements RolesPersistence {
serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
}
+ Set<String> fallbackEnabledRoles = roles.getFallbackEnabledRoles();
for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) {
String roleName = entry.getKey();
Set<String> roleHolders = entry.getValue();
+ boolean isFallbackEnabled = fallbackEnabledRoles.contains(roleName);
serializer.startTag(null, TAG_ROLE);
serializer.attribute(null, ATTRIBUTE_NAME, roleName);
+ serializer.attribute(null, ATTRIBUTE_FALLBACK_ENABLED,
+ Boolean.toString(isFallbackEnabled));
serializeRoleHolders(serializer, roleHolders);
serializer.endTag(null, TAG_ROLE);
}
diff --git a/service/java/com/android/role/persistence/RolesState.java b/service/java/com/android/role/persistence/RolesState.java
index f61efa0e8..a189dd4c2 100644
--- a/service/java/com/android/role/persistence/RolesState.java
+++ b/service/java/com/android/role/persistence/RolesState.java
@@ -16,10 +16,12 @@
package com.android.role.persistence;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemApi.Client;
+import android.permission.flags.Flags;
import java.util.Map;
import java.util.Objects;
@@ -33,7 +35,6 @@ import java.util.Set;
*/
@SystemApi(client = Client.SYSTEM_SERVER)
public final class RolesState {
-
/**
* The version of the roles.
*/
@@ -52,6 +53,12 @@ public final class RolesState {
private final Map<String, Set<String>> mRoles;
/**
+ * The names of roles with fallback enabled.
+ */
+ @NonNull
+ private final Set<String> mFallbackEnabledRoles;
+
+ /**
* Create a new instance of this class.
*
* @param version the version of the roles
@@ -60,9 +67,24 @@ public final class RolesState {
*/
public RolesState(int version, @Nullable String packagesHash,
@NonNull Map<String, Set<String>> roles) {
+ this(version, packagesHash, roles, roles.keySet());
+ }
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param version the version of the roles
+ * @param packagesHash the hash of all packages in the system
+ * @param roles the roles
+ * @param fallbackEnabledRoles the roles with fallback enabled
+ */
+ @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
+ public RolesState(int version, @Nullable String packagesHash,
+ @NonNull Map<String, Set<String>> roles, @NonNull Set<String> fallbackEnabledRoles) {
mVersion = version;
mPackagesHash = packagesHash;
mRoles = roles;
+ mFallbackEnabledRoles = fallbackEnabledRoles;
}
/**
@@ -94,6 +116,17 @@ public final class RolesState {
return mRoles;
}
+ /**
+ * Get the fallback enabled roles.
+ *
+ * @return fallback enabled roles
+ */
+ @NonNull
+ @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
+ public Set<String> getFallbackEnabledRoles() {
+ return mFallbackEnabledRoles;
+ }
+
@Override
public boolean equals(Object object) {
if (this == object) {
@@ -105,11 +138,12 @@ public final class RolesState {
RolesState that = (RolesState) object;
return mVersion == that.mVersion
&& Objects.equals(mPackagesHash, that.mPackagesHash)
- && Objects.equals(mRoles, that.mRoles);
+ && Objects.equals(mRoles, that.mRoles)
+ && Objects.equals(mFallbackEnabledRoles, that.mFallbackEnabledRoles);
}
@Override
public int hashCode() {
- return Objects.hash(mVersion, mPackagesHash, mRoles);
+ return Objects.hash(mVersion, mPackagesHash, mRoles, mFallbackEnabledRoles);
}
}
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..5c9dfa664 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,16 +40,16 @@ 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;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.data.SafetyCenterDataManager;
import java.time.Duration;
@@ -67,7 +65,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 +136,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 +159,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 +194,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 +206,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 +231,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 +267,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 +280,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();
@@ -330,37 +335,35 @@ final class SafetyCenterBroadcastDispatcher {
* lists of source IDs.
*
* <p>The set of user IDs (keys) is the profile parent user ID of {@code userProfileGroup} plus
- * the (possibly empty) set of running managed profile user IDs in that group.
- *
+ * all the other types of running profiles:
+ * <ol>
+ * <li>The (possibly empty) set of running managed profile user IDs in that group.
+ * <li>The (possibly empty) set of running private profile user ID in that group.
+ * </ol>
* <p>Every value present is a non-empty list, but the overall result may be empty.
*/
private SparseArray<List<String>> getUserIdsToSourceIds(
Broadcast broadcast,
UserProfileGroup userProfileGroup,
@RefreshReason int refreshReason) {
- int[] managedProfileIds = userProfileGroup.getManagedRunningProfilesUserIds();
- SparseArray<List<String>> result = new SparseArray<>(managedProfileIds.length + 1);
- List<String> profileParentSources =
- getSourceIdsForRefreshReason(
- refreshReason,
- broadcast.getSourceIdsForProfileParent(),
- broadcast.getSourceIdsForProfileParentOnPageOpen(),
- userProfileGroup.getProfileParentUserId());
-
- if (!profileParentSources.isEmpty()) {
- result.put(userProfileGroup.getProfileParentUserId(), profileParentSources);
- }
-
- for (int i = 0; i < managedProfileIds.length; i++) {
- List<String> managedProfileSources =
- getSourceIdsForRefreshReason(
- refreshReason,
- broadcast.getSourceIdsForManagedProfiles(),
- broadcast.getSourceIdsForManagedProfilesOnPageOpen(),
- managedProfileIds[i]);
-
- if (!managedProfileSources.isEmpty()) {
- result.put(managedProfileIds[i], managedProfileSources);
+ SparseArray<List<String>> result =
+ new SparseArray<>(userProfileGroup.getNumRunningProfiles());
+ for (int profilTypeIdx = 0;
+ profilTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profilTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profilTypeIdx];
+ int[] runningProfiles = userProfileGroup.getRunningProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < runningProfiles.length; ++profileIdx) {
+ List<String> profileSources =
+ getSourceIdsForRefreshReason(
+ refreshReason,
+ broadcast.getSourceIdsForProfileType(profileType),
+ broadcast.getSourceIdsOnPageOpenForProfileType(profileType),
+ runningProfiles[profileIdx]);
+
+ if (!profileSources.isEmpty()) {
+ result.put(runningProfiles[profileIdx], profileSources);
+ }
}
}
diff --git a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
index 92959a47d..641c242f1 100644
--- a/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
+++ b/service/java/com/android/safetycenter/SafetyCenterConfigReader.java
@@ -16,12 +16,14 @@
package com.android.safetycenter;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;
+import static java.util.Collections.emptyList;
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 +31,12 @@ 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.UserProfileGroup.ProfileType;
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 +53,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 +78,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 +116,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 +139,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);
@@ -179,16 +181,20 @@ public final class SafetyCenterConfigReader {
* source is expected to interact with Safety Center, but is currently being silenced / no-ops
* while an override for tests is in place.
*
- * <p>The {@code callingPackageName} is used to differentiate a real source being overridden. It
- * could be that a test is overriding a real source and as such the real source should not be
- * able to provide data while its override is in place.
+ * <p>The {@code callingPackageName} can be used to differentiate a real source being
+ * overridden. It could be that a test is overriding a real source and as such the real source
+ * should not be able to provide data while its override is in place.
*/
- public boolean isExternalSafetySourceActive(String safetySourceId, String callingPackageName) {
+ public boolean isExternalSafetySourceActive(
+ String safetySourceId, @Nullable String callingPackageName) {
ExternalSafetySource externalSafetySourceInCurrentConfig =
getCurrentConfigInternal().getExternalSafetySources().get(safetySourceId);
if (externalSafetySourceInCurrentConfig == null) {
return false;
}
+ if (callingPackageName == null) {
+ return true;
+ }
return Objects.equals(
externalSafetySourceInCurrentConfig.getSafetySource().getPackageName(),
callingPackageName);
@@ -225,26 +231,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;
}
}
@@ -415,7 +416,7 @@ public final class SafetyCenterConfigReader {
broadcast.mSourceIdsForProfileParentOnPageOpen.add(safetySource.getId());
}
boolean needsManagedProfilesBroadcast =
- SafetySources.supportsManagedProfiles(safetySource);
+ SafetySources.supportsProfileType(safetySource, PROFILE_TYPE_MANAGED);
if (needsManagedProfilesBroadcast) {
broadcast.mSourceIdsForManagedProfiles.add(safetySource.getId());
if (safetySource.isRefreshOnPageOpenAllowed()) {
@@ -423,6 +424,19 @@ public final class SafetyCenterConfigReader {
safetySource.getId());
}
}
+
+ // TODO(b/317378205): think about generalising these fields in Broadcast so that
+ // we are not duplicating the code - it can be a source of confusion and errors
+ // in future.
+ boolean needsPrivateProfileBroadcast =
+ SafetySources.supportsProfileType(safetySource, PROFILE_TYPE_PRIVATE);
+ if (needsPrivateProfileBroadcast) {
+ broadcast.mSourceIdsForPrivateProfile.add(safetySource.getId());
+ if (safetySource.isRefreshOnPageOpenAllowed()) {
+ broadcast.mSourceIdsForPrivateProfileOnPageOpen.add(
+ safetySource.getId());
+ }
+ }
}
}
@@ -491,6 +505,8 @@ public final class SafetyCenterConfigReader {
private final List<String> mSourceIdsForProfileParentOnPageOpen = new ArrayList<>();
private final List<String> mSourceIdsForManagedProfiles = new ArrayList<>();
private final List<String> mSourceIdsForManagedProfilesOnPageOpen = new ArrayList<>();
+ private final List<String> mSourceIdsForPrivateProfile = new ArrayList<>();
+ private final List<String> mSourceIdsForPrivateProfileOnPageOpen = new ArrayList<>();
private Broadcast(String packageName) {
mPackageName = packageName;
@@ -502,41 +518,42 @@ public final class SafetyCenterConfigReader {
}
/**
- * Returns the safety source ids associated with this broadcast in the profile owner.
+ * Returns the safety source ids associated with this broadcast in the given profile type.
*
- * <p>If this list is empty, there are no sources to dispatch to in the profile owner.
+ * <p>If this list is empty, there are no sources to dispatch to in the given profile type.
*/
- List<String> getSourceIdsForProfileParent() {
- return unmodifiableList(mSourceIdsForProfileParent);
- }
-
- /**
- * Returns the safety source ids associated with this broadcast in the profile owner that
- * have refreshOnPageOpenAllowed set to true in the XML config.
- *
- * <p>If this list is empty, there are no sources to dispatch to in the profile owner.
- */
- List<String> getSourceIdsForProfileParentOnPageOpen() {
- return unmodifiableList(mSourceIdsForProfileParentOnPageOpen);
- }
-
- /**
- * Returns the safety source ids associated with this broadcast in the managed profile(s).
- *
- * <p>If this list is empty, there are no sources to dispatch to in the managed profile(s).
- */
- List<String> getSourceIdsForManagedProfiles() {
- return unmodifiableList(mSourceIdsForManagedProfiles);
+ List<String> getSourceIdsForProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return unmodifiableList(mSourceIdsForProfileParent);
+ case PROFILE_TYPE_MANAGED:
+ return unmodifiableList(mSourceIdsForManagedProfiles);
+ case PROFILE_TYPE_PRIVATE:
+ return unmodifiableList(mSourceIdsForPrivateProfile);
+ default:
+ Log.w(TAG, "source ids asked for unexpected profile " + profileType);
+ return emptyList();
+ }
}
/**
- * Returns the safety source ids associated with this broadcast in the managed profile(s)
+ * Returns the safety source ids associated with this broadcast in the given profile type
* that have refreshOnPageOpenAllowed set to true in the XML config.
*
- * <p>If this list is empty, there are no sources to dispatch to in the managed profile(s).
+ * <p>If this list is empty, there are no sources to dispatch to in the given profile type.
*/
- List<String> getSourceIdsForManagedProfilesOnPageOpen() {
- return unmodifiableList(mSourceIdsForManagedProfilesOnPageOpen);
+ List<String> getSourceIdsOnPageOpenForProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return unmodifiableList(mSourceIdsForProfileParentOnPageOpen);
+ case PROFILE_TYPE_MANAGED:
+ return unmodifiableList(mSourceIdsForManagedProfilesOnPageOpen);
+ case PROFILE_TYPE_PRIVATE:
+ return unmodifiableList(mSourceIdsForPrivateProfileOnPageOpen);
+ default:
+ Log.w(TAG, "source ids asked for unexpected profile " + profileType);
+ return emptyList();
+ }
}
@Override
@@ -550,7 +567,10 @@ public final class SafetyCenterConfigReader {
that.mSourceIdsForProfileParentOnPageOpen)
&& mSourceIdsForManagedProfiles.equals(that.mSourceIdsForManagedProfiles)
&& mSourceIdsForManagedProfilesOnPageOpen.equals(
- that.mSourceIdsForManagedProfilesOnPageOpen);
+ that.mSourceIdsForManagedProfilesOnPageOpen)
+ && mSourceIdsForPrivateProfile.equals(that.mSourceIdsForPrivateProfile)
+ && mSourceIdsForPrivateProfileOnPageOpen.equals(
+ that.mSourceIdsForPrivateProfileOnPageOpen);
}
@Override
@@ -560,7 +580,9 @@ public final class SafetyCenterConfigReader {
mSourceIdsForProfileParent,
mSourceIdsForProfileParentOnPageOpen,
mSourceIdsForManagedProfiles,
- mSourceIdsForManagedProfilesOnPageOpen);
+ mSourceIdsForManagedProfilesOnPageOpen,
+ mSourceIdsForPrivateProfile,
+ mSourceIdsForPrivateProfileOnPageOpen);
}
@Override
@@ -576,6 +598,10 @@ public final class SafetyCenterConfigReader {
+ mSourceIdsForManagedProfiles
+ ", mSourceIdsForManagedProfilesOnPageOpen="
+ mSourceIdsForManagedProfilesOnPageOpen
+ + ", mSourceIdsForPrivateProfile="
+ + mSourceIdsForPrivateProfile
+ + ", mSourceIdsForPrivateProfileOnPageOpen="
+ + mSourceIdsForPrivateProfileOnPageOpen
+ '}';
}
}
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 e7edc1831..7c7ade3f1 100644
--- a/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
+++ b/service/java/com/android/safetycenter/SafetyCenterDataFactory.java
@@ -16,14 +16,18 @@
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.os.Build.VERSION_CODES.VANILLA_ICE_CREAM;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;
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,10 +54,11 @@ 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;
+import com.android.permission.flags.Flags;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.data.SafetyCenterDataManager;
import com.android.safetycenter.internaldata.SafetyCenterBundles;
import com.android.safetycenter.internaldata.SafetyCenterEntryId;
@@ -61,7 +66,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 +83,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterDataFactory {
@@ -87,7 +91,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 +100,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 +338,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);
@@ -388,42 +392,36 @@ public final class SafetyCenterDataFactory {
List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
List<SafetyCenterEntry> entries = new ArrayList<>(safetySources.size());
- for (int i = 0; i < safetySources.size(); i++) {
- SafetySource safetySource = safetySources.get(i);
-
- groupSafetyCenterEntryLevel =
- mergeSafetyCenterEntrySeverityLevels(
- groupSafetyCenterEntryLevel,
- addSafetyCenterEntry(
- safetyCenterOverallState,
- entries,
- safetySource,
- defaultPackageName,
- userProfileGroup.getProfileParentUserId(),
- false,
- false));
-
- if (!SafetySources.supportsManagedProfiles(safetySource)) {
- continue;
- }
-
- int[] managedProfilesUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int j = 0; j < managedProfilesUserIds.length; j++) {
- int managedProfileUserId = managedProfilesUserIds[j];
- boolean isManagedUserRunning =
- userProfileGroup.isManagedUserRunning(managedProfileUserId);
+ for (int safetySourceIdx = 0; safetySourceIdx < safetySources.size(); ++safetySourceIdx) {
+ SafetySource safetySource = safetySources.get(safetySourceIdx);
+ for (int profileTypeIdx = 0;
+ profileTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profileTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profileTypeIdx];
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
+ continue;
+ }
- groupSafetyCenterEntryLevel =
- mergeSafetyCenterEntrySeverityLevels(
- groupSafetyCenterEntryLevel,
- addSafetyCenterEntry(
- safetyCenterOverallState,
- entries,
- safetySource,
- defaultPackageName,
- managedProfileUserId,
- true,
- isManagedUserRunning));
+ int[] profileIds = userProfileGroup.getProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < profileIds.length; profileIdx++) {
+ int profileId = profileIds[profileIdx];
+ boolean isUserRunning =
+ userProfileGroup.containsRunningUserId(profileId, profileType);
+ if (profileType == PROFILE_TYPE_PRIVATE && !isUserRunning) {
+ continue;
+ }
+ groupSafetyCenterEntryLevel =
+ mergeSafetyCenterEntrySeverityLevels(
+ groupSafetyCenterEntryLevel,
+ addSafetyCenterEntry(
+ safetyCenterOverallState,
+ entries,
+ safetySource,
+ defaultPackageName,
+ profileId,
+ profileType,
+ isUserRunning));
+ }
}
}
@@ -443,7 +441,7 @@ public final class SafetyCenterDataFactory {
new SafetyCenterEntryOrGroup(
new SafetyCenterEntryGroup.Builder(
safetySourcesGroup.getId(),
- mSafetyCenterResourcesContext.getString(
+ mSafetyCenterResourcesApk.getString(
safetySourcesGroup.getTitleResId()))
.setSeverityLevel(groupSafetyCenterEntryLevel)
.setSummary(groupSummary)
@@ -509,13 +507,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 +524,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)) {
@@ -539,7 +533,8 @@ public final class SafetyCenterDataFactory {
SafetyCenterEntry entry = entries.get(i);
SafetyCenterEntryId entryId = SafetyCenterIds.entryIdFromString(entry.getId());
- if (UserUtils.isManagedProfile(entryId.getUserId(), mContext)) {
+ if (UserProfileGroup.getProfileTypeOfUser(entryId.getUserId(), mContext)
+ != PROFILE_TYPE_PRIMARY) {
continue;
}
@@ -563,15 +558,15 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
SafetyCenterEntry safetyCenterEntry =
toSafetyCenterEntry(
safetySource,
defaultPackageName,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
if (safetyCenterEntry == null) {
return SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED;
}
@@ -589,8 +584,8 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
switch (safetySource.getType()) {
case SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY:
return null;
@@ -599,7 +594,7 @@ public final class SafetyCenterDataFactory {
SafetySourceStatus safetySourceStatus =
getSafetySourceStatus(
mSafetyCenterDataManager.getSafetySourceDataInternal(key));
- boolean inQuietMode = isUserManaged && !isManagedUserRunning;
+ boolean inQuietMode = (PROFILE_TYPE_MANAGED == profileType) && !isUserRunning;
if (safetySourceStatus == null) {
int severityLevel =
inQuietMode
@@ -611,8 +606,8 @@ public final class SafetyCenterDataFactory {
severityLevel,
SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
}
PendingIntent sourceProvidedPendingIntent =
inQuietMode ? null : safetySourceStatus.getPendingIntent();
@@ -649,7 +644,7 @@ public final class SafetyCenterDataFactory {
.setSummary(
inQuietMode
? DevicePolicyResources.getWorkProfilePausedString(
- mSafetyCenterResourcesContext)
+ mSafetyCenterResourcesApk)
: safetySourceStatus.getSummary())
.setEnabled(enabled)
.setSeverityUnspecifiedIconType(severityUnspecifiedIconType)
@@ -670,8 +665,8 @@ public final class SafetyCenterDataFactory {
SafetyCenterEntry.ENTRY_SEVERITY_LEVEL_UNSPECIFIED,
SafetyCenterEntry.SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
}
Log.w(
TAG,
@@ -686,8 +681,8 @@ public final class SafetyCenterDataFactory {
@SafetyCenterEntry.EntrySeverityLevel int entrySeverityLevel,
@SafetyCenterEntry.SeverityUnspecifiedIconType int severityUnspecifiedIconType,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
if (SafetySources.isDefaultEntryHidden(safetySource)) {
return null;
}
@@ -697,7 +692,7 @@ public final class SafetyCenterDataFactory {
.setSafetySourceId(safetySource.getId())
.setUserId(userId)
.build();
- boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning;
+ boolean isQuietModeEnabled = (PROFILE_TYPE_MANAGED == profileType) && !isUserRunning;
PendingIntent pendingIntent =
mPendingIntentFactory.getPendingIntent(
safetySource.getId(),
@@ -707,23 +702,16 @@ public final class SafetyCenterDataFactory {
isQuietModeEnabled);
boolean enabled =
pendingIntent != null && !SafetySources.isDefaultEntryDisabled(safetySource);
- CharSequence title =
- isUserManaged
- ? DevicePolicyResources.getSafetySourceWorkString(
- mSafetyCenterResourcesContext,
- safetySource.getId(),
- safetySource.getTitleForWorkResId())
- : mSafetyCenterResourcesContext.getString(safetySource.getTitleResId());
+ CharSequence title = getTitleForProfileType(profileType, safetySource);
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)
@@ -744,38 +732,33 @@ public final class SafetyCenterDataFactory {
UserProfileGroup userProfileGroup) {
List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
List<SafetyCenterStaticEntry> staticEntries = new ArrayList<>(safetySources.size());
- for (int i = 0; i < safetySources.size(); i++) {
- SafetySource safetySource = safetySources.get(i);
-
- addSafetyCenterStaticEntry(
- staticEntriesToIds,
- safetyCenterOverallState,
- staticEntries,
- safetySource,
- defaultPackageName,
- userProfileGroup.getProfileParentUserId(),
- false,
- false);
-
- if (!SafetySources.supportsManagedProfiles(safetySource)) {
- continue;
- }
-
- int[] managedProfilesUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int j = 0; j < managedProfilesUserIds.length; j++) {
- int managedProfileUserId = managedProfilesUserIds[j];
- boolean isManagedUserRunning =
- userProfileGroup.isManagedUserRunning(managedProfileUserId);
-
- addSafetyCenterStaticEntry(
- staticEntriesToIds,
- safetyCenterOverallState,
- staticEntries,
- safetySource,
- defaultPackageName,
- managedProfileUserId,
- true,
- isManagedUserRunning);
+ for (int safetySourceIdx = 0; safetySourceIdx < safetySources.size(); safetySourceIdx++) {
+ SafetySource safetySource = safetySources.get(safetySourceIdx);
+ for (int profileTypeIdx = 0;
+ profileTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profileTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profileTypeIdx];
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
+ continue;
+ }
+ int[] profileIds = userProfileGroup.getProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < profileIds.length; ++profileIdx) {
+ int profileId = profileIds[profileIdx];
+ boolean isUserRunning =
+ userProfileGroup.containsRunningUserId(profileId, profileType);
+ if (profileType == PROFILE_TYPE_PRIVATE && !isUserRunning) {
+ continue;
+ }
+ addSafetyCenterStaticEntry(
+ staticEntriesToIds,
+ safetyCenterOverallState,
+ staticEntries,
+ safetySource,
+ defaultPackageName,
+ profileId,
+ profileType,
+ isUserRunning);
+ }
}
}
@@ -785,7 +768,7 @@ public final class SafetyCenterDataFactory {
safetyCenterStaticEntryGroups.add(
new SafetyCenterStaticEntryGroup(
- mSafetyCenterResourcesContext.getString(safetySourcesGroup.getTitleResId()),
+ mSafetyCenterResourcesApk.getString(safetySourcesGroup.getTitleResId()),
staticEntries));
}
@@ -796,15 +779,15 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
SafetyCenterStaticEntry staticEntry =
toSafetyCenterStaticEntry(
safetySource,
defaultPackageName,
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
if (staticEntry == null) {
return;
}
@@ -832,8 +815,8 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String defaultPackageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
switch (safetySource.getType()) {
case SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY:
return null;
@@ -842,7 +825,7 @@ public final class SafetyCenterDataFactory {
SafetySourceStatus safetySourceStatus =
getSafetySourceStatus(
mSafetyCenterDataManager.getSafetySourceDataInternal(key));
- boolean inQuietMode = isUserManaged && !isManagedUserRunning;
+ boolean inQuietMode = (profileType == PROFILE_TYPE_MANAGED) && !isUserRunning;
if (safetySourceStatus != null) {
PendingIntent sourceProvidedPendingIntent =
inQuietMode ? null : safetySourceStatus.getPendingIntent();
@@ -864,7 +847,7 @@ public final class SafetyCenterDataFactory {
.setSummary(
inQuietMode
? DevicePolicyResources.getWorkProfilePausedString(
- mSafetyCenterResourcesContext)
+ mSafetyCenterResourcesApk)
: safetySourceStatus.getSummary())
.setPendingIntent(entryPendingIntent)
.build();
@@ -873,15 +856,15 @@ public final class SafetyCenterDataFactory {
safetySource,
safetySource.getPackageName(),
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
case SafetySource.SAFETY_SOURCE_TYPE_STATIC:
return toDefaultSafetyCenterStaticEntry(
safetySource,
getStaticSourcePackageNameOrDefault(safetySource, defaultPackageName),
userId,
- isUserManaged,
- isManagedUserRunning);
+ profileType,
+ isUserRunning);
}
Log.w(TAG, "Unknown safety source type found in rigid group: " + safetySource.getType());
return null;
@@ -892,12 +875,12 @@ public final class SafetyCenterDataFactory {
SafetySource safetySource,
String packageName,
@UserIdInt int userId,
- boolean isUserManaged,
- boolean isManagedUserRunning) {
+ @ProfileType int profileType,
+ boolean isUserRunning) {
if (SafetySources.isDefaultEntryHidden(safetySource)) {
return null;
}
- boolean isQuietModeEnabled = isUserManaged && !isManagedUserRunning;
+ boolean isQuietModeEnabled = (profileType == PROFILE_TYPE_MANAGED) && !isUserRunning;
PendingIntent pendingIntent =
mPendingIntentFactory.getPendingIntent(
safetySource.getId(),
@@ -911,22 +894,15 @@ public final class SafetyCenterDataFactory {
return null;
}
- CharSequence title =
- isUserManaged
- ? DevicePolicyResources.getSafetySourceWorkString(
- mSafetyCenterResourcesContext,
- safetySource.getId(),
- safetySource.getTitleForWorkResId())
- : mSafetyCenterResourcesContext.getString(safetySource.getTitleResId());
+ CharSequence title = getTitleForProfileType(profileType, safetySource);
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 +1064,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 +1092,7 @@ public final class SafetyCenterDataFactory {
return "";
}
+ @TargetApi(UPSIDE_DOWN_CAKE)
private String getStatusTitleFromIssueCategories(
@Nullable SafetySourceIssueInfo topNonDismissedIssueInfo,
String deviceResourceName,
@@ -1125,7 +1101,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 +1109,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,10 +1143,10 @@ 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 getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues);
@@ -1206,14 +1177,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);
@@ -1232,7 +1203,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);
@@ -1247,13 +1218,36 @@ 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);
return null;
}
+ private CharSequence getTitleForProfileType(
+ @ProfileType int profileType, SafetySource safetySource) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ case PROFILE_TYPE_MANAGED:
+ return DevicePolicyResources.getSafetySourceWorkString(
+ mSafetyCenterResourcesApk,
+ safetySource.getId(),
+ safetySource.getTitleForWorkResId());
+ case PROFILE_TYPE_PRIVATE:
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ return mSafetyCenterResourcesApk.getString(
+ safetySource.getTitleForPrivateProfileResId());
+ }
+ Log.w(TAG, "unsupported private profile type encountered");
+ return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ default:
+ Log.w(TAG, "unexpected value for the profile type " + profileType);
+ return mSafetyCenterResourcesApk.getString(safetySource.getTitleResId());
+ }
+ }
+
private static SafetySourceKey toSafetySourceKey(String safetyCenterEntryIdString) {
SafetyCenterEntryId id = SafetyCenterIds.entryIdFromString(safetyCenterEntryIdString);
return SafetySourceKey.of(id.getSafetySourceId(), id.getUserId());
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..091daa0a6 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);
}
}
@@ -91,10 +87,14 @@ final class SafetyCenterListeners {
*/
void deliverDataForUserProfileGroup(UserProfileGroup userProfileGroup) {
ArrayMap<String, SafetyCenterData> safetyCenterDataCache = new ArrayMap<>();
- int[] relevantUserIds = userProfileGroup.getProfileParentAndManagedRunningProfilesUserIds();
+ int[] relevantUserIds = userProfileGroup.getAllRunningProfilesUserIds();
for (int i = 0; i < relevantUserIds.length; i++) {
deliverUpdateForUser(
- relevantUserIds[i], userProfileGroup, safetyCenterDataCache, true, null);
+ relevantUserIds[i],
+ userProfileGroup,
+ safetyCenterDataCache,
+ /* updateSafetyCenterData= */ true,
+ /* safetyCenterErrorDetails= */ null);
}
}
@@ -105,13 +105,13 @@ final class SafetyCenterListeners {
void deliverErrorForUserProfileGroup(
UserProfileGroup userProfileGroup, SafetyCenterErrorDetails safetyCenterErrorDetails) {
ArrayMap<String, SafetyCenterData> safetyCenterDataCache = new ArrayMap<>();
- int[] relevantUserIds = userProfileGroup.getProfileParentAndManagedRunningProfilesUserIds();
+ int[] relevantUserIds = userProfileGroup.getAllRunningProfilesUserIds();
for (int i = 0; i < relevantUserIds.length; i++) {
deliverUpdateForUser(
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..cc23b4e02 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,9 +32,8 @@ 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;
import java.io.PrintWriter;
@@ -53,7 +50,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterRefreshTracker {
private static final String TAG = "SafetyCenterRefreshTrac";
@@ -78,15 +74,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 +154,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 +163,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 +173,8 @@ public final class SafetyCenterRefreshTracker {
int sourceResult = toSystemEventResult(successful);
SafetyCenterStatsdLogger.writeSourceRefreshSystemEvent(
requestType,
- sourceId,
- UserUtils.isManagedProfile(userId, mContext),
+ safetySourceKey.getSourceId(),
+ UserProfileGroup.getProfileTypeOfUser(safetySourceKey.getUserId(), mContext),
duration,
sourceResult,
refreshReason,
@@ -237,7 +232,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 +267,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);
@@ -279,12 +283,21 @@ public final class SafetyCenterRefreshTracker {
SafetyCenterStatsdLogger.writeSourceRefreshSystemEvent(
requestType,
sourceKey.getSourceId(),
- UserUtils.isManagedProfile(sourceKey.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(sourceKey.getUserId(), mContext),
duration,
SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT,
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 +311,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 +319,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 +337,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 +442,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 +471,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..c9a5e1c03 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,18 @@ 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.flags.Flags;
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 +89,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 +103,6 @@ import javax.annotation.concurrent.NotThreadSafe;
* @hide
*/
@Keep
-@RequiresApi(TIRAMISU)
public final class SafetyCenterService extends SystemService {
private static final String TAG = "SafetyCenterService";
@@ -112,9 +112,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 +121,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 +133,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 +144,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 +183,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 +191,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);
+ }
+
+ @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() {
+ 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 +316,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);
@@ -298,9 +349,8 @@ public final class SafetyCenterService extends SystemService {
String safetySourceId, String packageName, @UserIdInt int userId) {
requireNonNull(safetySourceId);
requireNonNull(packageName);
- getContext()
- .enforceCallingOrSelfPermission(
- SEND_SAFETY_CENTER_UPDATE, "getSafetySourceData");
+ enforceAnyCallingOrSelfPermissions(
+ "getSafetySourceData", SEND_SAFETY_CENTER_UPDATE, MANAGE_SAFETY_CENTER);
if (!enforceCrossUserPermission("getSafetySourceData", userId)
|| !enforcePackage(Binder.getCallingUid(), packageName, userId)
|| !checkApiEnabled("getSafetySourceData")) {
@@ -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,60 +1071,143 @@ 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);
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) {
+ // These intents are available on V+ only, and are called for managed and other
+ // profile(s).
+ filter.addAction(Intent.ACTION_PROFILE_ADDED);
+ filter.addAction(Intent.ACTION_PROFILE_REMOVED);
+ filter.addAction(Intent.ACTION_PROFILE_AVAILABLE);
+ filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
+ } else {
+ // Only these intents are available in T and U, but that's okay because only managed
+ // profiles are supported by Safety Center on these SDK versions.
+ 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(
+ /* 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();
- if (!UserProfileGroup.isSupported(userId, context)) {
- Log.i(
- TAG,
- "Received broadcast for user id "
- + userId
- + ", which is an unsupported user");
+ Log.d(TAG, "Received action: " + action + ", for user id: " + userId);
+
+ if (!isUserIdValidForAction(action, userId, context)) {
+ return;
+ }
+
+ if (isUserOrProfileRemoved(action)) {
+ removeUserAndData(userId);
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);
- break;
- case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
- removeUser(userId, false);
- // fall through!
- case Intent.ACTION_USER_ADDED:
- case Intent.ACTION_MANAGED_PROFILE_ADDED:
- case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
- startRefreshingSafetySources(REFRESH_REASON_OTHER, userId);
+
+ if (isProfileUnavailable(action)) {
+ removeUser(userId);
+ return;
+ }
+
+ if (Intent.ACTION_USER_SWITCHED.equals(action) || isProfileAddedOrAvailable(action)) {
+ synchronized (mApiLock) {
+ startRefreshingSafetySourcesLocked(REFRESH_REASON_OTHER, userId);
mNotificationChannels.createAllChannelsForUser(getContext(), userHandle);
- break;
+ }
+ return;
}
+ Log.w(TAG, "Received unexpected broadcast with action: " + action);
+ }
+ }
+
+ private static boolean isUserIdValidForAction(
+ String action, @UserIdInt int userId, Context context) {
+ if (!UserProfileGroup.isSupported(userId, context)) {
+ Log.i(
+ TAG,
+ "Received broadcast for user id: "
+ + userId
+ + ", which is an unsupported user");
+ return false;
+ }
+ if (Intent.ACTION_USER_SWITCHED.equals(action)
+ && userId != ActivityManager.getCurrentUser()) {
+ Log.w(
+ TAG,
+ "Received broadcast for user id: "
+ + userId
+ + ", which is not the current user");
+ return false;
}
+ if (isProfileAddedOrAvailable(action) && !UserUtils.isUserExistent(userId, context)) {
+ Log.w(
+ TAG,
+ "Received broadcast for user id: "
+ + userId
+ + ", which does not exist");
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isUserOrProfileRemoved(String action) {
+ if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ return true;
+ }
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) {
+ return Intent.ACTION_PROFILE_REMOVED.equals(action);
+ }
+ return Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action);
+ }
+
+ private static boolean isProfileUnavailable(String action) {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) {
+ return Intent.ACTION_PROFILE_UNAVAILABLE.equals(action);
+ }
+ return Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action);
+ }
+
+ private static boolean isProfileAddedOrAvailable(String action) {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileSupported()) {
+ return Intent.ACTION_PROFILE_AVAILABLE.equals(action)
+ || Intent.ACTION_PROFILE_ADDED.equals(action);
+ }
+ return Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action);
+ }
+
+ 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) {
@@ -1053,31 +1225,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 +1276,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 +1305,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 +1329,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..7be0ef00e 100644
--- a/service/java/com/android/safetycenter/SafetySources.java
+++ b/service/java/com/android/safetycenter/SafetySources.java
@@ -16,20 +16,17 @@
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;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
/**
* A helper class to facilitate working with {@link SafetySource} objects.
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetySources {
private static final String TAG = "SafetySources";
@@ -53,6 +50,23 @@ public final class SafetySources {
/** Returns whether a {@link SafetySource} supports managed profiles. */
public static boolean supportsManagedProfiles(SafetySource safetySource) {
+ return supportsAllProfiles(safetySource);
+ }
+
+ /**
+ * Returns whether a {@link SafetySource} supports the profile of the given type
+ * {@code profileType}.
+ */
+ public static boolean supportsProfileType(
+ SafetySource safetySource, @ProfileType int profileType) {
+ if (UserProfileGroup.PROFILE_TYPE_PRIMARY == profileType) {
+ return true;
+ }
+ return supportsAllProfiles(safetySource);
+ }
+
+ /** Returns whether a {@link SafetySource} supports all profiles. */
+ private static boolean supportsAllProfiles(SafetySource safetySource) {
int safetySourceProfile = safetySource.getProfile();
switch (safetySourceProfile) {
case SafetySource.PROFILE_PRIMARY:
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..46a440bf7 100644
--- a/service/java/com/android/safetycenter/UserProfileGroup.java
+++ b/service/java/com/android/safetycenter/UserProfileGroup.java
@@ -16,11 +16,9 @@
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.IntDef;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -30,10 +28,12 @@ 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;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -45,22 +45,47 @@ import java.util.Objects;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
+//TODO(b/286539356) Do not expose the private profile when it's not running.
public final class UserProfileGroup {
private static final String TAG = "UserProfileGroup";
+ // UserHandle#USER_NULL is a @TestApi so it cannot be accessed from the mainline module.
+ public static final @UserIdInt int USER_NULL = -10000;
@UserIdInt private final int mProfileParentUserId;
private final int[] mManagedProfilesUserIds;
private final int[] mManagedRunningProfilesUserIds;
+ @UserIdInt private final int mPrivateProfileUserId;
+ private final boolean mPrivateProfileRunning;
+
+ /** Respresents the profile type of the primary user. */
+ public static final int PROFILE_TYPE_PRIMARY = 0;
+ /** Respresents the profile type of the managed profile. */
+ public static final int PROFILE_TYPE_MANAGED = 1;
+ /** Respresents the profile type of the private profile. */
+ public static final int PROFILE_TYPE_PRIVATE = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {PROFILE_TYPE_PRIMARY, PROFILE_TYPE_MANAGED, PROFILE_TYPE_PRIVATE})
+ public @interface ProfileType {
+ // This array needs to cover all profile types. So whenever a new entry is added above then
+ // please remember to include it in this array as well.
+ int[] ALL_PROFILE_TYPES =
+ {PROFILE_TYPE_PRIMARY, PROFILE_TYPE_MANAGED, PROFILE_TYPE_PRIVATE};
+ }
+
private UserProfileGroup(
@UserIdInt int profileParentUserId,
int[] managedProfilesUserIds,
- int[] managedRunningProfilesUserIds) {
+ int[] managedRunningProfilesUserIds,
+ @UserIdInt int privateProfileUserId,
+ boolean privateProfileRunning) {
mProfileParentUserId = profileParentUserId;
mManagedProfilesUserIds = managedProfilesUserIds;
mManagedRunningProfilesUserIds = managedRunningProfilesUserIds;
+ mPrivateProfileUserId = privateProfileUserId;
+ mPrivateProfileRunning = privateProfileRunning;
}
/** Returns all the alive {@link UserProfileGroup}s. */
@@ -121,6 +146,10 @@ public final class UserProfileGroup {
int[] managedRunningProfilesUserIds = new int[userProfiles.size()];
int managedProfilesUserIdsLen = 0;
int managedRunningProfilesUserIdsLen = 0;
+
+ int privateProfileUserId = USER_NULL;
+ boolean privateProfileRunning = false;
+
for (int i = 0; i < userProfiles.size(); i++) {
UserHandle userProfileHandle = userProfiles.get(i);
int userProfileId = userProfileHandle.getIdentifier();
@@ -131,19 +160,23 @@ public final class UserProfileGroup {
managedRunningProfilesUserIds[managedRunningProfilesUserIdsLen++] =
userProfileId;
}
+ } else if (UserUtils.isPrivateProfile(userProfileId, context)) {
+ privateProfileUserId = userProfileId;
+ privateProfileRunning = UserUtils.isProfileRunning(userProfileId, context);
}
}
- UserProfileGroup userProfileGroup =
- new UserProfileGroup(
- profileParentUserId,
- Arrays.copyOf(managedProfilesUserIds, managedProfilesUserIdsLen),
- Arrays.copyOf(
- managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen));
+ UserProfileGroup userProfileGroup = new UserProfileGroup(
+ profileParentUserId,
+ Arrays.copyOf(managedProfilesUserIds, managedProfilesUserIdsLen),
+ Arrays.copyOf(managedRunningProfilesUserIds, managedRunningProfilesUserIdsLen),
+ privateProfileUserId,
+ privateProfileRunning
+ );
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;
@@ -154,7 +187,8 @@ public final class UserProfileGroup {
if (!isProfile(userId, context)) {
return true;
}
- return UserUtils.isManagedProfile(userId, context);
+ return UserUtils.isManagedProfile(userId, context)
+ || UserUtils.isPrivateProfile(userId, context);
}
private static UserManager getUserManagerForUser(@UserIdInt int userId, Context context) {
@@ -167,7 +201,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);
}
@@ -211,32 +246,141 @@ public final class UserProfileGroup {
return mProfileParentUserId;
}
- /** Returns the managed profile user ids of the {@link UserProfileGroup}. */
- public int[] getManagedProfilesUserIds() {
- return mManagedProfilesUserIds;
- }
+ /**
+ * A convenience method to get all the profile ids of all the users of all profile types. So, in
+ * essence, this is equivalent to iterating through all the profile types using
+ * {@link ProfileType#ALL_PROFILE_TYPES} and getting all the users for each of the profile type
+ * using {@link #getProfilesOfType(int profileType)}
+ */
+ public int[] getAllProfilesUserIds() {
+ int[] allProfileIds = new int[getNumProfiles()];
+ allProfileIds[0] = mProfileParentUserId;
+ System.arraycopy(
+ mManagedProfilesUserIds,
+ /* srcPos= */ 0,
+ allProfileIds,
+ /* destPos= */ 1,
+ mManagedProfilesUserIds.length);
+
+ if (mPrivateProfileUserId != USER_NULL) {
+ allProfileIds[allProfileIds.length - 1] = mPrivateProfileUserId;
+ }
- /** Returns the running managed profile user ids of the {@link UserProfileGroup}. */
- public int[] getManagedRunningProfilesUserIds() {
- return mManagedRunningProfilesUserIds;
+ return allProfileIds;
}
/**
- * Convenience method that combines the results of {@link
- * UserProfileGroup#getProfileParentUserId()} and {@link
- * UserProfileGroup#getManagedRunningProfilesUserIds()}.
+ * A convenience method to get all the profile ids of all the users (that are currently running)
+ * of all profile types. So, in essence, this is equivalent to iterating through all the profile
+ * {types using {@link ProfileType#ALL_PROFILE_TYPES} and getting all the users for each of the
+ * profile type using {@link #getProfilesOfType(int profileType)} only if they are running.
*/
- public int[] getProfileParentAndManagedRunningProfilesUserIds() {
- int[] profileParentAndManagedRunningProfilesUserIds =
- new int[mManagedRunningProfilesUserIds.length + 1];
- profileParentAndManagedRunningProfilesUserIds[0] = mProfileParentUserId;
+ public int[] getAllRunningProfilesUserIds() {
+ int[] allRunningProfileIds = new int[getNumRunningProfiles()];
+ allRunningProfileIds[0] = mProfileParentUserId;
System.arraycopy(
mManagedRunningProfilesUserIds,
- 0,
- profileParentAndManagedRunningProfilesUserIds,
- 1,
+ /* srcPos= */ 0,
+ allRunningProfileIds,
+ /* destPos= */ 1,
mManagedRunningProfilesUserIds.length);
- return profileParentAndManagedRunningProfilesUserIds;
+
+ if (mPrivateProfileRunning) {
+ allRunningProfileIds[allRunningProfileIds.length - 1] = mPrivateProfileUserId;
+ }
+
+ return allRunningProfileIds;
+ }
+
+ /**
+ * Returns the profiles of the specified type. Returns an empty array if no profile of the
+ * specified type exists.
+ */
+ public int[] getProfilesOfType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return new int[] {mProfileParentUserId};
+ case PROFILE_TYPE_MANAGED:
+ return mManagedProfilesUserIds;
+ case PROFILE_TYPE_PRIVATE:
+ return mPrivateProfileUserId != USER_NULL
+ ? new int[]{mPrivateProfileUserId} : new int[]{};
+ default:
+ Log.w(TAG, "profiles requested for unexpected profile type " + profileType);
+ return new int[] {};
+ }
+ }
+
+ /**
+ * Returns the running profiles of the specified type. Returns an empty array if no profile of
+ * the specified type exists.
+ */
+ public int[] getRunningProfilesOfType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return new int[] {mProfileParentUserId};
+ case PROFILE_TYPE_MANAGED:
+ return mManagedRunningProfilesUserIds;
+ case PROFILE_TYPE_PRIVATE:
+ //TODO(b/286539356) add the new feature flag protection when available.
+ return mPrivateProfileRunning
+ ? new int[] {mPrivateProfileUserId} : new int[] {};
+ default:
+ Log.w(TAG, "Unexpected profile type " + profileType);
+ return new int[] {};
+ }
+ }
+
+ /** Returns the total number of running profiles in this user profile group */
+ public int getNumRunningProfiles() {
+ return 1
+ + mManagedRunningProfilesUserIds.length
+ + (mPrivateProfileRunning ? 1 : 0);
+ }
+
+ /** Returns the total number of profiles in this user profile group */
+ private int getNumProfiles() {
+ return 1
+ + mManagedProfilesUserIds.length
+ + (mPrivateProfileUserId == USER_NULL ? 0 : 1);
+ }
+
+ /**
+ * Returns the {@link ProfileType} for the provided {@code userId}. Note that the provided
+ * {@code userId} must be supported by the {@link UserProfileGroup} i.e.
+ * {@link #isSupported(int, Context)} should return true for {@code userId}.
+ */
+ public static @ProfileType int getProfileTypeOfUser(@UserIdInt int userId, Context context) {
+ if (UserUtils.isManagedProfile(userId, context)) {
+ return PROFILE_TYPE_MANAGED;
+ }
+ if (UserUtils.isPrivateProfile(userId, context)) {
+ return PROFILE_TYPE_PRIVATE;
+ }
+ return PROFILE_TYPE_PRIMARY;
+ }
+
+ /**
+ * Returns true iff the given userId is contained in this {@link UserProfileGroup} and it's
+ * running.
+ */
+ boolean containsRunningUserId(@UserIdInt int userId, @ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return true;
+ case PROFILE_TYPE_MANAGED:
+ for (int i = 0; i < mManagedRunningProfilesUserIds.length; i++) {
+ if (mManagedRunningProfilesUserIds[i] == userId) {
+ return true;
+ }
+ }
+ return false;
+ case PROFILE_TYPE_PRIVATE:
+ return mPrivateProfileRunning;
+ default:
+ Log.w(TAG, "Unexpected profile type " + profileType);
+ return false;
+ }
}
/** Returns whether the {@link UserProfileGroup} contains the given {@code userId}. */
@@ -251,17 +395,7 @@ public final class UserProfileGroup {
}
}
- return false;
- }
-
- /** Returns whether the given {@code userId} is associated with a running managed profile. */
- boolean isManagedUserRunning(@UserIdInt int userId) {
- for (int i = 0; i < mManagedRunningProfilesUserIds.length; i++) {
- if (userId == mManagedRunningProfilesUserIds[i]) {
- return true;
- }
- }
- return false;
+ return USER_NULL != mPrivateProfileUserId && userId == mPrivateProfileUserId;
}
@Override
@@ -272,7 +406,9 @@ public final class UserProfileGroup {
return mProfileParentUserId == that.mProfileParentUserId
&& Arrays.equals(mManagedProfilesUserIds, that.mManagedProfilesUserIds)
&& Arrays.equals(
- mManagedRunningProfilesUserIds, that.mManagedRunningProfilesUserIds);
+ mManagedRunningProfilesUserIds, that.mManagedRunningProfilesUserIds)
+ && mPrivateProfileUserId == that.mPrivateProfileUserId
+ && mPrivateProfileRunning == that.mPrivateProfileRunning;
}
@Override
@@ -280,7 +416,9 @@ public final class UserProfileGroup {
return Objects.hash(
mProfileParentUserId,
Arrays.hashCode(mManagedProfilesUserIds),
- Arrays.hashCode(mManagedRunningProfilesUserIds));
+ Arrays.hashCode(mManagedRunningProfilesUserIds),
+ mPrivateProfileUserId,
+ mPrivateProfileRunning);
}
@Override
@@ -292,6 +430,10 @@ public final class UserProfileGroup {
+ Arrays.toString(mManagedProfilesUserIds)
+ ", mManagedRunningProfilesUserIds="
+ Arrays.toString(mManagedRunningProfilesUserIds)
+ + ", mPrivateProfileUserId"
+ + mPrivateProfileUserId
+ + ", mPrivateProfileRunning"
+ + mPrivateProfileRunning
+ '}';
}
}
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..7385a2c3b 100644
--- a/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
+++ b/service/java/com/android/safetycenter/data/SafetyCenterDataManager.java
@@ -16,11 +16,11 @@
package com.android.safetycenter.data;
-import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static android.Manifest.permission.MANAGE_SAFETY_CENTER;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
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 +30,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;
@@ -40,6 +41,7 @@ import com.android.safetycenter.SafetyCenterRefreshTracker;
import com.android.safetycenter.SafetySourceIssueInfo;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.internaldata.SafetyCenterIssueActionId;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
@@ -60,12 +62,12 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterDataManager {
private static final String TAG = "SafetyCenterDataManager";
+ private final Context mContext;
private final SafetyCenterRefreshTracker mSafetyCenterRefreshTracker;
private final SafetySourceDataRepository mSafetySourceDataRepository;
private final SafetyCenterIssueDismissalRepository mSafetyCenterIssueDismissalRepository;
@@ -81,6 +83,7 @@ public final class SafetyCenterDataManager {
SafetyCenterConfigReader safetyCenterConfigReader,
SafetyCenterRefreshTracker safetyCenterRefreshTracker,
ApiLock apiLock) {
+ mContext = context;
mSafetyCenterRefreshTracker = safetyCenterRefreshTracker;
mSafetyCenterInFlightIssueActionRepository =
new SafetyCenterInFlightIssueActionRepository(context);
@@ -88,7 +91,6 @@ public final class SafetyCenterDataManager {
new SafetyCenterIssueDismissalRepository(apiLock, safetyCenterConfigReader);
mSafetySourceDataRepository =
new SafetySourceDataRepository(
- context,
mSafetyCenterInFlightIssueActionRepository,
mSafetyCenterIssueDismissalRepository);
mSafetyCenterIssueRepository =
@@ -134,10 +136,14 @@ public final class SafetyCenterDataManager {
String packageName,
@UserIdInt int userId) {
if (!mSafetySourceDataValidator.validateRequest(
- safetySourceData, safetySourceId, packageName, userId)) {
+ safetySourceData,
+ /* callerCanAccessAnySource= */ false,
+ 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 +153,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 +169,12 @@ public final class SafetyCenterDataManager {
}
mSafetySourceStateCollectedLogger.writeSourceUpdatedAtom(
- key, safetySourceData, refreshReason, sourceDataDiffers, userId, safetyEvent);
+ safetySourceKey,
+ safetySourceData,
+ refreshReason,
+ sourceDataDiffers,
+ userId,
+ safetyEvent);
return safetyCenterDataChanged;
}
@@ -199,11 +214,15 @@ public final class SafetyCenterDataManager {
String packageName,
@UserIdInt int userId) {
if (!mSafetySourceDataValidator.validateRequest(
- null, safetySourceId, packageName, userId)) {
+ /* safetySourceData= */ null,
+ /* callerCanAccessAnySource= */ false,
+ 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 +232,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 +248,12 @@ public final class SafetyCenterDataManager {
}
mSafetySourceStateCollectedLogger.writeSourceUpdatedAtom(
- key, null, refreshReason, sourceDataDiffers, userId, safetyEvent);
+ safetySourceKey,
+ /* safetySourceData= */ null,
+ refreshReason,
+ sourceDataDiffers,
+ userId,
+ safetyEvent);
return safetyCenterDataChanged;
}
@@ -389,6 +417,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 ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -406,8 +439,14 @@ public final class SafetyCenterDataManager {
@Nullable
public SafetySourceData getSafetySourceData(
String safetySourceId, String packageName, @UserIdInt int userId) {
+ boolean callerCanAccessAnySource =
+ mContext.checkCallingOrSelfPermission(MANAGE_SAFETY_CENTER) == PERMISSION_GRANTED;
if (!mSafetySourceDataValidator.validateRequest(
- null, safetySourceId, packageName, userId)) {
+ /* safetySourceData= */ null,
+ callerCanAccessAnySource,
+ safetySourceId,
+ packageName,
+ userId)) {
return null;
}
return mSafetySourceDataRepository.getSafetySourceData(
@@ -472,38 +511,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()
@@ -530,7 +568,7 @@ public final class SafetyCenterDataManager {
* Writes a SafetySourceStateCollected atom for the given source in response to a stats pull.
*/
public void logSafetySourceStateCollectedAutomatic(
- SafetySourceKey sourceKey, boolean isManagedProfile) {
- mSafetySourceStateCollectedLogger.writeAutomaticAtom(sourceKey, isManagedProfile);
+ SafetySourceKey sourceKey, @ProfileType int profileType) {
+ mSafetySourceStateCollectedLogger.writeAutomaticAtom(sourceKey, profileType);
}
}
diff --git a/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java b/service/java/com/android/safetycenter/data/SafetyCenterInFlightIssueActionRepository.java
index 4fa6e5363..39809aa6f 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.UserProfileGroup;
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 {
@@ -90,7 +87,7 @@ final class SafetyCenterInFlightIssueActionRepository {
SafetyCenterStatsdLogger.writeInlineActionSystemEvent(
issueKey.getSafetySourceId(),
- UserUtils.isManagedProfile(issueKey.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(issueKey.getUserId(), mContext),
issueTypeId,
duration,
result);
@@ -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..3806584a8 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,15 +30,13 @@ 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;
import com.android.safetycenter.SafetySourceIssueInfo;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.SafetySources;
import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import java.io.PrintWriter;
@@ -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,26 +87,13 @@ 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));
+ updateIssues(userId, UserProfileGroup.getProfileTypeOfUser(userId, mContext));
}
- private void updateIssues(@UserIdInt int userId, boolean isManagedProfile) {
+ private void updateIssues(@UserIdInt int userId, @ProfileType int profileType) {
List<SafetySourceIssueInfo> issues =
- getAllStoredIssuesFromRawSourceData(userId, isManagedProfile);
+ getAllStoredIssuesFromRawSourceData(userId, profileType);
issues.sort(SAFETY_SOURCE_ISSUES_INFO_BY_SEVERITY_DESCENDING);
@@ -201,14 +183,14 @@ final class SafetyCenterIssueRepository {
}
private List<SafetySourceIssueInfo> getAllStoredIssuesFromRawSourceData(
- @UserIdInt int userId, boolean isManagedProfile) {
+ @UserIdInt int userId, @ProfileType int profileType) {
List<SafetySourceIssueInfo> allIssuesInfo = new ArrayList<>();
List<SafetySourcesGroup> safetySourcesGroups =
mSafetyCenterConfigReader.getSafetySourcesGroups();
for (int j = 0; j < safetySourcesGroups.size(); j++) {
addSafetySourceIssuesInfo(
- allIssuesInfo, safetySourcesGroups.get(j), userId, isManagedProfile);
+ allIssuesInfo, safetySourcesGroups.get(j), userId, profileType);
}
return allIssuesInfo;
@@ -218,7 +200,7 @@ final class SafetyCenterIssueRepository {
List<SafetySourceIssueInfo> issuesInfo,
SafetySourcesGroup safetySourcesGroup,
@UserIdInt int userId,
- boolean isManagedProfile) {
+ @ProfileType int profileType) {
List<SafetySource> safetySources = safetySourcesGroup.getSafetySources();
for (int i = 0; i < safetySources.size(); i++) {
SafetySource safetySource = safetySources.get(i);
@@ -226,7 +208,7 @@ final class SafetyCenterIssueRepository {
if (!SafetySources.isExternal(safetySource)) {
continue;
}
- if (isManagedProfile && !SafetySources.supportsManagedProfiles(safetySource)) {
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
continue;
}
@@ -262,12 +244,11 @@ final class SafetyCenterIssueRepository {
* UserProfileGroup}.
*/
private List<SafetySourceIssueInfo> getIssuesFor(UserProfileGroup userProfileGroup) {
- List<SafetySourceIssueInfo> issues =
- new ArrayList<>(getIssuesForUser(userProfileGroup.getProfileParentUserId()));
+ List<SafetySourceIssueInfo> issues = new ArrayList<>();
- int[] managedRunningProfileUserIds = userProfileGroup.getManagedRunningProfilesUserIds();
- for (int i = 0; i < managedRunningProfileUserIds.length; i++) {
- issues.addAll(getIssuesForUser(managedRunningProfileUserIds[i]));
+ int[] allRunningProfileUserIds = userProfileGroup.getAllRunningProfilesUserIds();
+ for (int i = 0; i < allRunningProfileUserIds.length; i++) {
+ issues.addAll(getIssuesForUser(allRunningProfileUserIds[i]));
}
return issues;
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..4b74b0440 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,13 +26,13 @@ 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;
import com.android.safetycenter.SafetyCenterConfigReader;
import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetySources;
+import com.android.safetycenter.UserProfileGroup;
import java.util.List;
import java.util.Set;
@@ -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;
@@ -74,9 +70,12 @@ final class SafetySourceDataValidator {
*
* @param safetySourceData being set, or {@code null} if retrieving or clearing data, or
* reporting an error
+ * @param callerCanAccessAnySource whether we should allow the caller to access any source, or
+ * restrict them to their own {@code packageName}
*/
boolean validateRequest(
@Nullable SafetySourceData safetySourceData,
+ boolean callerCanAccessAnySource,
String safetySourceId,
String packageName,
@UserIdInt int userId) {
@@ -87,18 +86,24 @@ final class SafetySourceDataValidator {
}
SafetySource safetySource = externalSafetySource.getSafetySource();
- validateCallingPackage(safetySource, packageName, safetySourceId);
+ if (!callerCanAccessAnySource) {
+ validateCallingPackage(safetySource, packageName, safetySourceId);
+ }
- if (UserUtils.isManagedProfile(userId, mContext)
- && !SafetySources.supportsManagedProfiles(safetySource)) {
+ @UserProfileGroup.ProfileType int profileType =
+ UserProfileGroup.getProfileTypeOfUser(userId, mContext);
+ if (!SafetySources.supportsProfileType(safetySource, profileType)) {
throw new IllegalArgumentException(
- "Unexpected managed profile request for safety source: " + safetySourceId);
+ "Unexpected profile type: "
+ + profileType
+ + " for safety source: "
+ + safetySourceId);
}
boolean retrievingOrClearingData = safetySourceData == null;
if (retrievingOrClearingData) {
- return mSafetyCenterConfigReader.isExternalSafetySourceActive(
- safetySourceId, packageName);
+ return isExternalSafetySourceActive(
+ callerCanAccessAnySource, safetySourceId, packageName);
}
SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
@@ -110,6 +115,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);
@@ -164,7 +171,20 @@ final class SafetySourceDataValidator {
}
}
- return mSafetyCenterConfigReader.isExternalSafetySourceActive(safetySourceId, packageName);
+ return isExternalSafetySourceActive(callerCanAccessAnySource, safetySourceId, packageName);
+ }
+
+ private boolean isExternalSafetySourceActive(
+ boolean callerCanAccessAnySource, String safetySourceId, String callerPackageName) {
+ boolean isActive =
+ mSafetyCenterConfigReader.isExternalSafetySourceActive(
+ safetySourceId, callerCanAccessAnySource ? null : callerPackageName);
+ if (!isActive) {
+ Log.i(
+ TAG,
+ "Call ignored as safety source " + safetySourceId + " is not currently active");
+ }
+ return isActive;
}
private void validateCallingPackage(
@@ -192,13 +212,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 +230,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..1bf8685bf 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,11 +24,12 @@ 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;
import com.android.safetycenter.SafetySourceKey;
+import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.internaldata.SafetyCenterIssueKey;
import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
@@ -44,7 +42,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 {
@@ -67,13 +64,13 @@ final class SafetySourceStateCollectedLogger {
/**
* Writes a SafetySourceStateCollected atom for the given source in response to a stats pull.
*/
- void writeAutomaticAtom(SafetySourceKey sourceKey, boolean isManagedProfile) {
+ void writeAutomaticAtom(SafetySourceKey sourceKey, @ProfileType int profileType) {
logSafetySourceStateCollected(
sourceKey,
mSourceDataRepository.getSafetySourceData(sourceKey),
/* refreshReason= */ null,
/* sourceDataDiffers= */ false,
- isManagedProfile,
+ profileType,
/* safetyEvent= */ null,
mSourceDataRepository.getSafetySourceLastUpdated(sourceKey));
}
@@ -94,7 +91,7 @@ final class SafetySourceStateCollectedLogger {
safetySourceData,
refreshReason,
sourceDataDiffers,
- UserUtils.isManagedProfile(userId, mContext),
+ UserProfileGroup.getProfileTypeOfUser(userId, mContext),
safetyEvent,
/* lastUpdatedElapsedTimeMillis= */ null);
}
@@ -104,7 +101,7 @@ final class SafetySourceStateCollectedLogger {
@Nullable SafetySourceData sourceData,
@Nullable @SafetyCenterManager.RefreshReason Integer refreshReason,
boolean sourceDataDiffers,
- boolean isManagedProfile,
+ @ProfileType int profileType,
@Nullable SafetyEvent safetyEvent,
@Nullable @ElapsedRealtimeLong Long lastUpdatedElapsedTimeMillis) {
SafetySourceStatus sourceStatus = sourceData == null ? null : sourceData.getStatus();
@@ -132,10 +129,11 @@ final class SafetySourceStateCollectedLogger {
}
}
+ Integer severityLevel = maxSeverityLevel > Integer.MIN_VALUE ? maxSeverityLevel : null;
SafetyCenterStatsdLogger.writeSafetySourceStateCollected(
sourceKey.getSourceId(),
- isManagedProfile,
- maxSeverityLevel > Integer.MIN_VALUE ? maxSeverityLevel : null,
+ profileType,
+ 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..a8dc7568e 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;
@@ -42,6 +38,7 @@ import com.android.safetycenter.SafetyCenterFlags;
import com.android.safetycenter.SafetySourceKey;
import com.android.safetycenter.SafetySources;
import com.android.safetycenter.UserProfileGroup;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import com.android.safetycenter.data.SafetyCenterDataManager;
import java.util.List;
@@ -56,7 +53,6 @@ import java.util.List;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback {
private static final String TAG = "SafetyCenterPullAtom";
@@ -93,17 +89,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 +107,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);
}
}
@@ -154,18 +150,19 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
continue;
}
- writeSafetySourceStateCollectedAtomLocked(
- loggableSource,
- userProfileGroup.getProfileParentUserId(),
- /* isUserManaged= */ false);
-
- if (!SafetySources.supportsManagedProfiles(loggableSource)) {
- continue;
- }
-
- int[] managedIds = userProfileGroup.getManagedRunningProfilesUserIds();
- for (int k = 0; k < managedIds.length; k++) {
- writeSafetySourceStateCollectedAtomLocked(loggableSource, managedIds[k], true);
+ for (int profileTypeIdx = 0;
+ profileTypeIdx < ProfileType.ALL_PROFILE_TYPES.length;
+ ++profileTypeIdx) {
+ @ProfileType int profileType = ProfileType.ALL_PROFILE_TYPES[profileTypeIdx];
+ if (!SafetySources.supportsProfileType(loggableSource, profileType)) {
+ continue;
+ }
+
+ int[] profileIds = userProfileGroup.getProfilesOfType(profileType);
+ for (int profileIdx = 0; profileIdx < profileIds.length; profileIdx++) {
+ writeSafetySourceStateCollectedAtomLocked(
+ loggableSource, profileIds[profileIdx], profileType);
+ }
}
}
}
@@ -173,8 +170,8 @@ public final class SafetyCenterPullAtomCallback implements StatsPullAtomCallback
@GuardedBy("mApiLock")
private void writeSafetySourceStateCollectedAtomLocked(
- SafetySource safetySource, @UserIdInt int userId, boolean isUserManaged) {
+ SafetySource safetySource, @UserIdInt int userId, @ProfileType int profileType) {
SafetySourceKey sourceKey = SafetySourceKey.of(safetySource.getId(), userId);
- mDataManager.logSafetySourceStateCollectedAutomatic(sourceKey, isUserManaged);
+ mDataManager.logSafetySourceStateCollectedAutomatic(sourceKey, profileType);
}
}
diff --git a/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java b/service/java/com/android/safetycenter/logging/SafetyCenterStatsdLogger.java
index 8ca662d27..3311d0c1f 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;
@@ -27,6 +25,8 @@ import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTIO
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__NAVIGATION_SOURCE__SOURCE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SENSOR__SENSOR_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_INTERACTION_REPORTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
@@ -45,12 +45,15 @@ import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVE
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__RESULT__TIMEOUT;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
import static com.android.permission.PermissionStatsLog.SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__AUTOMATIC;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__COLLECTION_TYPE__SOURCE_UPDATED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_CRITICAL_WARNING;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_SOURCE_STATE_COLLECTED__SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
@@ -71,10 +74,12 @@ import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SE
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_LEVEL_UNKNOWN;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_OK;
import static com.android.permission.PermissionStatsLog.SAFETY_STATE__OVERALL_SEVERITY_LEVEL__SAFETY_SEVERITY_RECOMMENDATION;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_MANAGED;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIMARY;
+import static com.android.safetycenter.UserProfileGroup.PROFILE_TYPE_PRIVATE;
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,10 +88,11 @@ 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;
+import com.android.safetycenter.UserProfileGroup.ProfileType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -101,7 +107,6 @@ import java.time.Duration;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterStatsdLogger {
private static final String TAG = "SafetyCenterStatsdLog";
@@ -165,7 +170,7 @@ public final class SafetyCenterStatsdLogger {
/** Writes a {@link PermissionStatsLog#SAFETY_SOURCE_STATE_COLLECTED} atom. */
public static void writeSafetySourceStateCollected(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
@Nullable @SafetySourceData.SeverityLevel Integer sourceSeverityLevel,
long openIssuesCount,
long dismissedIssuesCount,
@@ -185,7 +190,7 @@ public final class SafetyCenterStatsdLogger {
PermissionStatsLog.write(
SAFETY_SOURCE_STATE_COLLECTED,
idStringToLong(sourceId),
- toSourceStateCollectedProfileType(isManagedProfile),
+ toSourceStateCollectedProfileType(profileType),
toSafetySourceStateCollectedSeverityLevel(sourceSeverityLevel),
openIssuesCount,
dismissedIssuesCount,
@@ -207,7 +212,7 @@ public final class SafetyCenterStatsdLogger {
public static void writeSourceRefreshSystemEvent(
@RefreshRequestType int refreshType,
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
Duration duration,
@SystemEventResult int result,
long refreshReason,
@@ -219,7 +224,7 @@ public final class SafetyCenterStatsdLogger {
SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
toSourceRefreshEventType(refreshType),
idStringToLong(sourceId),
- toSystemEventProfileType(isManagedProfile),
+ toSystemEventProfileType(profileType),
UNSET_ISSUE_TYPE_ID,
duration.toMillis(),
result,
@@ -258,7 +263,7 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeInlineActionSystemEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
@Nullable String issueTypeId,
Duration duration,
@SystemEventResult int result) {
@@ -269,7 +274,7 @@ public final class SafetyCenterStatsdLogger {
SAFETY_CENTER_SYSTEM_EVENT_REPORTED,
SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__INLINE_ACTION,
idStringToLong(sourceId),
- toSystemEventProfileType(isManagedProfile),
+ toSystemEventProfileType(profileType),
issueTypeId == null ? UNSET_ISSUE_TYPE_ID : idStringToLong(issueTypeId),
duration.toMillis(),
result,
@@ -283,13 +288,13 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeNotificationPostedEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel) {
writeNotificationInteractionReportedEvent(
SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_POSTED,
sourceId,
- isManagedProfile,
+ profileType,
issueTypeId,
sourceSeverityLevel);
}
@@ -300,13 +305,13 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeNotificationDismissedEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel) {
writeNotificationInteractionReportedEvent(
SAFETY_CENTER_INTERACTION_REPORTED__ACTION__NOTIFICATION_DISMISSED,
sourceId,
- isManagedProfile,
+ profileType,
issueTypeId,
sourceSeverityLevel);
}
@@ -317,7 +322,7 @@ public final class SafetyCenterStatsdLogger {
*/
public static void writeNotificationActionClickedEvent(
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel,
boolean isPrimaryAction) {
@@ -326,13 +331,13 @@ public final class SafetyCenterStatsdLogger {
? SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_PRIMARY_ACTION_CLICKED
: SAFETY_CENTER_INTERACTION_REPORTED__ACTION__ISSUE_SECONDARY_ACTION_CLICKED;
writeNotificationInteractionReportedEvent(
- action, sourceId, isManagedProfile, issueTypeId, sourceSeverityLevel);
+ action, sourceId, profileType, issueTypeId, sourceSeverityLevel);
}
private static void writeNotificationInteractionReportedEvent(
int interactionReportedAction,
String sourceId,
- boolean isManagedProfile,
+ @ProfileType int profileType,
String issueTypeId,
@SafetySourceData.SeverityLevel int sourceSeverityLevel) {
if (!SafetyCenterFlags.getAllowStatsdLogging()) {
@@ -346,7 +351,7 @@ public final class SafetyCenterStatsdLogger {
SAFETY_CENTER_INTERACTION_REPORTED__NAVIGATION_SOURCE__SOURCE_UNKNOWN,
toInteractionReportedSeverityLevel(sourceSeverityLevel),
idStringToLong(sourceId),
- toInteractionReportedProfileType(isManagedProfile),
+ toInteractionReportedProfileType(profileType),
idStringToLong(issueTypeId),
SAFETY_CENTER_INTERACTION_REPORTED__SENSOR__SENSOR_UNKNOWN,
UNSET_SOURCE_GROUP_ID,
@@ -386,22 +391,43 @@ public final class SafetyCenterStatsdLogger {
return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__EVENT_TYPE__EVENT_TYPE_UNKNOWN;
}
- private static int toSourceStateCollectedProfileType(boolean isManagedProfile) {
- return isManagedProfile
- ? SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED
- : SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ private static int toSourceStateCollectedProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ case PROFILE_TYPE_MANAGED:
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
+ case PROFILE_TYPE_PRIVATE:
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+ }
+ Log.w(TAG, "state collect arg requested for unknown profile type " + profileType);
+ return SAFETY_SOURCE_STATE_COLLECTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
}
- private static int toSystemEventProfileType(boolean isManagedProfile) {
- return isManagedProfile
- ? SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED
- : SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ private static int toSystemEventProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ case PROFILE_TYPE_MANAGED:
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
+ case PROFILE_TYPE_PRIVATE:
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+ }
+ Log.w(TAG, "system event arg requested for unknown profile type " + profileType);
+ return SAFETY_CENTER_SYSTEM_EVENT_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
}
- private static int toInteractionReportedProfileType(boolean isManagedProfile) {
- return isManagedProfile
- ? SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED
- : SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ private static int toInteractionReportedProfileType(@ProfileType int profileType) {
+ switch (profileType) {
+ case PROFILE_TYPE_PRIMARY:
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PERSONAL;
+ case PROFILE_TYPE_MANAGED:
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_MANAGED;
+ case PROFILE_TYPE_PRIVATE:
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_PRIVATE;
+ }
+ Log.w(TAG, "interaction enum requested for unknown profile type " + profileType);
+ return SAFETY_CENTER_INTERACTION_REPORTED__SAFETY_SOURCE_PROFILE_TYPE__PROFILE_TYPE_UNKNOWN;
}
/**
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 5308cdae8..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,7 +64,10 @@ 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;
}
@@ -80,9 +77,9 @@ public final class SafetyCenterNotificationChannels {
// 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);
@@ -106,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));
@@ -119,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;
}
/**
@@ -153,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());
@@ -165,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"));
@@ -204,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 a8da70cf0..84001f249 100644
--- a/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java
+++ b/service/java/com/android/safetycenter/notifications/SafetyCenterNotificationFactory.java
@@ -16,7 +16,6 @@
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;
@@ -24,7 +23,6 @@ import static android.safetycenter.SafetyCenterManager.EXTRA_SAFETY_SOURCE_USER_
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;
@@ -32,7 +30,6 @@ 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;
@@ -40,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;
@@ -56,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;
}
/**
@@ -89,6 +84,10 @@ final class SafetyCenterNotificationFactory {
SafetySourceIssue issue,
SafetySourceIssue.Action action,
@UserIdInt int userId) {
+ if (action.getSuccessMessage() == null) {
+ return null;
+ }
+
String channelId = mNotificationChannels.getCreatedChannelId(notificationManager, issue);
if (channelId == null) {
return null;
@@ -107,7 +106,8 @@ final class SafetyCenterNotificationFactory {
.setContentTitle(action.getSuccessMessage())
.setShowWhen(true)
.setTimeoutAfter(SUCCESS_NOTIFICATION_TIMEOUT.toMillis())
- .setContentIntent(contentIntent);
+ .setContentIntent(contentIntent)
+ .setAutoCancel(true);
Integer color = getNotificationColor(SafetySourceData.SEVERITY_LEVEL_INFORMATION);
if (color != null) {
@@ -175,6 +175,10 @@ final class SafetyCenterNotificationFactory {
builder.addAction(notificationAction);
}
+ if (issue.getSeverityLevel() == SafetySourceData.SEVERITY_LEVEL_INFORMATION) {
+ builder.setAutoCancel(true);
+ }
+
return builder.build();
}
@@ -231,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:
@@ -247,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);
}
@@ -262,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(
@@ -270,7 +277,7 @@ final class SafetyCenterNotificationFactory {
if (issueAction.willResolve()) {
return getReceiverPendingIntentForResolvingAction(issueKey, issueAction);
} else {
- return getDirectPendingIntentForNonResolvingAction(issueKey, issueAction);
+ return getDirectPendingIntentForNonResolvingAction(issueAction);
}
}
@@ -291,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..6cfa39580 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,15 +24,15 @@ 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;
import com.android.safetycenter.ApiLock;
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 +51,6 @@ import com.android.safetycenter.logging.SafetyCenterStatsdLogger;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
private static final String TAG = "SafetyCenterNR";
@@ -120,13 +116,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 +158,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:
@@ -211,7 +213,7 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
if (dismissedIssue != null) {
SafetyCenterStatsdLogger.writeNotificationDismissedEvent(
issueKey.getSafetySourceId(),
- UserUtils.isManagedProfile(userId, context),
+ UserProfileGroup.getProfileTypeOfUser(userId, context),
dismissedIssue.getIssueTypeId(),
dismissedIssue.getSeverityLevel());
}
@@ -237,19 +239,11 @@ public final class SafetyCenterNotificationReceiver extends BroadcastReceiver {
if (issue != null) {
SafetyCenterStatsdLogger.writeNotificationActionClickedEvent(
issueKey.getSafetySourceId(),
- UserUtils.isManagedProfile(issueKey.getUserId(), context),
+ UserProfileGroup.getProfileTypeOfUser(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 72b1f0e95..2e298fa90 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,18 @@ 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 +64,6 @@ import javax.annotation.concurrent.NotThreadSafe;
*
* @hide
*/
-@RequiresApi(TIRAMISU)
@NotThreadSafe
public final class SafetyCenterNotificationSender {
@@ -120,13 +117,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 +134,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 +142,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 +177,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;
@@ -210,11 +209,9 @@ public final class SafetyCenterNotificationSender {
/** Updates Safety Center notifications for the given {@link UserProfileGroup}. */
public void updateNotifications(UserProfileGroup userProfileGroup) {
- updateNotifications(userProfileGroup.getProfileParentUserId());
-
- int[] managedProfileUserIds = userProfileGroup.getManagedProfilesUserIds();
- for (int i = 0; i < managedProfileUserIds.length; i++) {
- updateNotifications(managedProfileUserIds[i]);
+ int[] allProfilesUserIds = userProfileGroup.getAllProfilesUserIds();
+ for (int i = 0; i < allProfilesUserIds.length; i++) {
+ updateNotifications(allProfilesUserIds[i]);
}
}
@@ -224,6 +221,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 +284,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 +323,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);
@@ -380,7 +384,7 @@ public final class SafetyCenterNotificationSender {
mNotifiedIssues.put(key, issue);
SafetyCenterStatsdLogger.writeNotificationPostedEvent(
key.getSafetySourceId(),
- UserUtils.isManagedProfile(key.getUserId(), mContext),
+ UserProfileGroup.getProfileTypeOfUser(key.getUserId(), mContext),
issue.getIssueTypeId(),
issue.getSeverityLevel());
}
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/service/lint-baseline.xml b/service/lint-baseline.xml
index 90ea8d411..b10928320 100644
--- a/service/lint-baseline.xml
+++ b/service/lint-baseline.xml
@@ -1,5 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 33): `getDeduplicationGroup`"
+ errorLine1=" String deduplicationGroup = issueInfo.getSafetySource().getDeduplicationGroup();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java"
+ line="316"
+ column="65"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 33): `getDeduplicationId`"
+ errorLine1=" String deduplicationId = issueInfo.getSafetySourceIssue().getDeduplicationId();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/safetycenter/data/SafetyCenterIssueDeduplicator.java"
+ line="317"
+ column="67"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="Call requires API level 34 (current min is 33): `android.content.pm.PackageManager#getPackageUidAsUser`"
+ errorLine1=" packageManager.getPackageUidAsUser("
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/safetycenter/SafetyCenterService.java"
+ line="732"
+ column="40"/>
+ </issue>
<issue
id="NewApi"
@@ -13,14 +46,113 @@
</issue>
<issue
- id="NewApi"
- message="Call requires API level 34 (current min is 33): `android.content.pm.PackageManager#getPackageUidAsUser`"
- errorLine1=" packageManager.getPackageUidAsUser("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
+ id="FlaggedApi"
+ message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `writeFile` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" roles = new RolesState(mVersion, packagesHash,"
+ errorLine2=" ^">
<location
- file="packages/modules/Permission/service/java/com/android/safetycenter/SafetyCenterService.java"
- line="660"
+ file="packages/modules/Permission/service/java/com/android/role/RoleUserState.java"
+ line="450"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFallbackEnabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `readFile` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" fallbackEnabledRoles = roleState.getFallbackEnabledRoles();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/RoleUserState.java"
+ line="468"
+ column="40"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `parseRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new RolesState(version, packagesHash, roles, fallbackEnabledRoles);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java"
+ line="167"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFallbackEnabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `serializeRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" Set&lt;String> fallbackEnabledRoles = roles.getFallbackEnabledRoles();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java"
+ line="247"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `RolesState` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(version, packagesHash, roles, roles.keySet());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/persistence/RolesState.java"
+ line="70"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `writeFile` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" roles = new RolesState(mVersion, packagesHash,"
+ errorLine2=" ^">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/RoleUserState.java"
+ line="450"
+ column="21"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFallbackEnabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `readFile` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" fallbackEnabledRoles = roleState.getFallbackEnabledRoles();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/RoleUserState.java"
+ line="468"
column="40"/>
</issue>
+ <issue
+ id="FlaggedApi"
+ message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `parseRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" return new RolesState(version, packagesHash, roles, fallbackEnabledRoles);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java"
+ line="167"
+ column="16"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `getFallbackEnabledRoles()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `serializeRoles` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" Set&lt;String> fallbackEnabledRoles = roles.getFallbackEnabledRoles();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java"
+ line="247"
+ column="44"/>
+ </issue>
+
+ <issue
+ id="FlaggedApi"
+ message="Method `RolesState()` is a flagged API and should be inside an `if (Flags.systemServerRoleControllerEnabled())` check (or annotate the surrounding method `RolesState` with `@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) to transfer requirement to caller`)"
+ errorLine1=" this(version, packagesHash, roles, roles.keySet());"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="packages/modules/Permission/service/java/com/android/role/persistence/RolesState.java"
+ line="70"
+ column="9"/>
+ </issue>
+
</issues> \ No newline at end of file
diff --git a/service/proguard.flags b/service/proguard.flags
new file mode 100644
index 000000000..a504239a1
--- /dev/null
+++ b/service/proguard.flags
@@ -0,0 +1,4 @@
+# Keep classes that implements RoleBehavior, which are used by reflection.
+-keep class * implements com.android.role.controller.model.RoleBehavior {
+ *;
+} \ No newline at end of file
diff --git a/service/proto/role_service.proto b/service/proto/role_service.proto
index 79c422992..f982ead5b 100644
--- a/service/proto/role_service.proto
+++ b/service/proto/role_service.proto
@@ -53,4 +53,7 @@ message RoleProto {
// The package names of the holders of this role.
repeated string holders = 2;
+
+ // Whether fallback holders are enabled for this role.
+ optional bool fallback_enabled = 3;
}
diff --git a/testing/Android.bp b/testing/Android.bp
index 8abed1e0c..d74510754 100644
--- a/testing/Android.bp
+++ b/testing/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/apex/Android.bp b/tests/apex/Android.bp
index 126fcb4b2..83bf4e252 100644
--- a/tests/apex/Android.bp
+++ b/tests/apex/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
index 1806f8e13..6500b3926 100644
--- a/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ b/tests/apex/java/com/android/role/persistence/RolesPersistenceTest.kt
@@ -20,7 +20,6 @@ import android.content.ApexEnvironment
import android.content.Context
import android.os.Process
import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.google.common.truth.Truth.assertThat
@@ -29,6 +28,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
@@ -37,27 +37,36 @@ import org.mockito.MockitoAnnotations.initMocks
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-@RunWith(AndroidJUnit4::class)
+@RunWith(Parameterized::class)
class RolesPersistenceTest {
private val context = InstrumentationRegistry.getInstrumentation().context
private lateinit var mockDataDirectory: File
-
private lateinit var mockitoSession: MockitoSession
@Mock lateinit var apexEnvironment: ApexEnvironment
+ @Parameterized.Parameter(0) lateinit var stateVersion: StateVersion
+ private lateinit var state: RolesState
private val persistence = RolesPersistenceImpl {}
- private val state = RolesState(1, "packagesHash", mapOf("role" to setOf("holder1", "holder2")))
+ private val defaultRoles = mapOf(ROLE_NAME to setOf(HOLDER_1, HOLDER_2))
+ private val stateVersionUndefined = RolesState(VERSION_UNDEFINED, PACKAGE_HASH, defaultRoles)
+ private val stateVersionFallbackMigrated =
+ RolesState(VERSION_FALLBACK_MIGRATED, PACKAGE_HASH, defaultRoles, setOf(ROLE_NAME))
private val user = Process.myUserHandle()
@Before
- fun createMockDataDirectory() {
+ fun setUp() {
+ createMockDataDirectory()
+ mockApexEnvironment()
+ state = getState()
+ }
+
+ private fun createMockDataDirectory() {
mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE)
mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() }
}
- @Before
- fun mockApexEnvironment() {
+ private fun mockApexEnvironment() {
initMocks(this)
mockitoSession =
mockitoSession()
@@ -80,7 +89,7 @@ class RolesPersistenceTest {
persistence.writeForUser(state, user)
val persistedState = persistence.readForUser(user)
- checkPersistedState(persistedState)
+ assertThat(persistedState).isEqualTo(state)
}
@Test
@@ -91,7 +100,7 @@ class RolesPersistenceTest {
.writeText("<roles version=\"-1\"><role name=\"com.foo.bar\"><holder")
val persistedState = persistence.readForUser(user)
- checkPersistedState(persistedState!!)
+ assertThat(persistedState).isEqualTo(state)
}
@Test
@@ -103,14 +112,28 @@ class RolesPersistenceTest {
assertThat(persistedState).isNull()
}
- 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)
+ private fun getState(): RolesState =
+ when (stateVersion) {
+ StateVersion.VERSION_UNDEFINED -> stateVersionUndefined
+ StateVersion.VERSION_FALLBACK_MIGRATED -> stateVersionFallbackMigrated
+ }
+
+ enum class StateVersion {
+ VERSION_UNDEFINED,
+ VERSION_FALLBACK_MIGRATED
}
companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): Array<StateVersion> = StateVersion.values()
+
+ private const val VERSION_UNDEFINED = -1
+ private const val VERSION_FALLBACK_MIGRATED = 1
private const val APEX_MODULE_NAME = "com.android.permission"
+ private const val PACKAGE_HASH = "packagesHash"
+ private const val ROLE_NAME = "roleName"
+ private const val HOLDER_1 = "holder1"
+ private const val HOLDER_2 = "holder2"
}
}
diff --git a/tests/cts/permission/Android.bp b/tests/cts/permission/Android.bp
new file mode 100644
index 000000000..9636ceab6
--- /dev/null
+++ b/tests/cts/permission/Android.bp
@@ -0,0 +1,130 @@
+//
+// Copyright (C) 2008 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: "CtsPermissionTestCases",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ // Include both the 32 and 64 bit versions
+ compile_multilib: "both",
+ static_libs: [
+ "ctstestrunner-axt",
+ "guava",
+ "android-ex-camera2",
+ "compatibility-device-util-axt",
+ "truth",
+ "androidx.annotation_annotation",
+ "platformprotosnano",
+ "permission-test-util-lib",
+ "nativetesthelper",
+ // TODO(b/175251166): remove once Android migrates to JUnit 4.12,
+ // which provides assertThrows
+ "testng",
+ "bluetooth-test-util-lib",
+ "CtsAccessibilityCommon",
+ "safety-center-internal-data",
+ "sts-device-util",
+ "platform-test-rules",
+ "CtsVirtualDeviceCommonLib",
+ "android.permission.flags-aconfig-java-export",
+ "androidx.test.rules",
+ ],
+ jni_libs: [
+ "libctspermission_jni",
+ "libpermissionmanager_native_test",
+ "libnativehelper_compat_libc++",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ "src/**/*.kt",
+ ],
+ sdk_version: "test_current",
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ data: [
+ ":AppThatDefinesUndefinedPermissionGroupElement",
+ ":AppThatDoesNotHaveBgLocationAccess",
+ ":CtsAdversarialPermissionDefinerApp",
+ ":CtsAdversarialPermissionUserApp",
+ ":CtsAppThatAccessesLocationOnCommand",
+ ":CtsAppThatAlsoDefinesPermissionA",
+ ":CtsAppThatAlsoDefinesPermissionADifferentCert",
+ ":CtsAppThatAlsoDefinesPermissionGroupADifferentCert",
+ ":CtsAppThatAlsoDefinesPermissionGroupADifferentCert30",
+ ":CtsAppThatDefinesPermissionA",
+ ":CtsAppThatDefinesPermissionInPlatformGroup",
+ ":CtsAppThatDefinesPermissionWithInvalidGroup",
+ ":CtsAppThatDefinesPermissionWithInvalidGroup30",
+ ":CtsAppThatHasNotificationListener",
+ ":CtsAppThatRequestsBluetoothPermission30",
+ ":CtsAppThatRequestsCalendarContactsBodySensorCustomPermission",
+ ":CtsAppThatRequestsBluetoothPermission31",
+ ":CtsAppThatRequestsBluetoothPermissionNeverForLocation31",
+ ":CtsAppThatRequestsContactsAndCallLogPermission16",
+ ":CtsAppThatRequestsContactsPermission15",
+ ":CtsAppThatRequestsContactsPermission16",
+ ":CtsAppThatRequestsLocationAndBackgroundPermission28",
+ ":CtsAppThatRequestsLocationAndBackgroundPermission29",
+ ":CtsAppThatRequestsBluetoothPermissionNeverForLocationNoProvider",
+ ":CtsAppThatRequestsLocationPermission22",
+ ":CtsAppThatRequestsLocationPermission28",
+ ":CtsAppThatRequestsLocationPermission29",
+ ":CtsAppThatRequestsLocationPermission29v4",
+ ":CtsAppThatRequestsOneTimePermission",
+ ":CtsAppThatRequestsPermissionAandB",
+ ":CtsAppThatRequestsPermissionAandC",
+ ":CtsAppThatRequestsStoragePermission22",
+ ":CtsAppThatRequestsStoragePermission28",
+ ":CtsAppThatRequestsStoragePermission29",
+ ":CtsAppThatRunsRationaleTests",
+ ":CtsAppToTestRevokeSelfPermission",
+ ":CtsAppWithSharedUidThatRequestsLocationPermission28",
+ ":CtsAppWithSharedUidThatRequestsLocationPermission29",
+ ":CtsAppWithSharedUidThatRequestsNoPermissions",
+ ":CtsAppWithSharedUidThatRequestsPermissions",
+ ":CtsInstallPermissionDefinerApp",
+ ":CtsInstallPermissionEscalatorApp",
+ ":CtsInstallPermissionUserApp",
+ ":CtsRuntimePermissionDefinerApp",
+ ":CtsRuntimePermissionUserApp",
+ ":CtsStorageEscalationApp28",
+ ":CtsStorageEscalationApp29Full",
+ ":CtsStorageEscalationApp29Scoped",
+ ":CtsVictimPermissionDefinerApp",
+ ":CtsAppThatRequestsMultiplePermissionsWithMinMaxSdk",
+ ":CtsAppThatRequestsSystemAlertWindow22",
+ ":CtsAppThatRequestsSystemAlertWindow23",
+ ":CtsAppThatRequestCustomCameraPermission",
+ ":CtsAppThatRequestsDevicePermissions",
+ ],
+ per_testcase_directory: true,
+}
diff --git a/tests/cts/permission/AndroidManifest.xml b/tests/cts/permission/AndroidManifest.xml
new file mode 100644
index 000000000..43fd97bb2
--- /dev/null
+++ b/tests/cts/permission/AndroidManifest.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2007 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.permission.cts"
+ android:targetSandboxVersion="2">
+
+ <!-- for android.permission.cts.PermissionGroupChange -->
+ <permission android:name="android.permission.cts.B"
+ android:protectionLevel="dangerous"
+ android:label="@string/perm_b"
+ android:permissionGroup="android.permission.cts.groupB"
+ android:description="@string/perm_b"/>
+
+ <!-- for android.permission.cts.PermissionGroupChange -->
+ <permission android:name="android.permission.cts.C"
+ android:protectionLevel="dangerous"
+ android:label="@string/perm_c"
+ android:permissionGroup="android.permission.cts.groupC"
+ android:description="@string/perm_c"/>
+
+ <!-- for android.permission.cts.LocationAccessCheckTest -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+
+ <!-- for android.permission.cts.NearbyDevicesRenouncePermissionTest -->
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
+
+ <!-- for android.permission.cts.PermissionGroupChange -->
+ <permission-group android:description="@string/perm_group_b"
+ android:label="@string/perm_group_b"
+ android:name="android.permission.cts.groupB"/>
+
+ <!-- for android.permission.cts.PermissionGroupChange -->
+ <permission-group android:description="@string/perm_group_c"
+ android:label="@string/perm_group_c"
+ android:name="android.permission.cts.groupC"/>
+
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.permission.cts.PermissionStubActivity"
+ android:label="PermissionStubActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+
+ <service android:name="android.permission.cts.CtsNotificationListenerService"
+ android:exported="true"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService"/>
+ </intent-filter>
+ </service>
+ <service android:name=".AccessibilityTestService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService"/>
+ </intent-filter>
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/test_accessibilityservice"/>
+ </service>
+ </application>
+
+ <!--
+ The CTS stubs package cannot be used as the target application here,
+ since that requires many permissions to be set. Instead, specify this
+ package itself as the target and include any stub activities needed.
+
+ This test package uses the default InstrumentationTestRunner, because
+ the InstrumentationCtsTestRunner is only available in the stubs
+ package. That runner cannot be added to this package either, since it
+ relies on hidden APIs.
+ -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permission.cts"
+ android:label="CTS tests of android.permission">
+ </instrumentation>
+
+</manifest>
diff --git a/tests/cts/permission/AndroidTest.xml b/tests/cts/permission/AndroidTest.xml
new file mode 100644
index 000000000..0d012db7d
--- /dev/null
+++ b/tests/cts/permission/AndroidTest.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Permission test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="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="token" value="SIM_CARD" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <!-- Keep screen on for Bluetooth scanning -->
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ <option name="screen-always-on" value="on" />
+ <option name="disable-device-config-sync" value="true" />
+ </target_preparer>
+
+ <!-- Ensure device provisioning and user setup are marked as completed -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="settings put global device_provisioned 1" />
+ <option name="run-command" value="settings put secure user_setup_complete 1" />
+ </target_preparer>
+
+ <!-- Install main test suite apk -->
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionTestCases.apk" />
+ </target_preparer>
+
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-permission" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permission"/>
+ </target_preparer>
+
+ <!-- Collect screen recordings -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/user/0/android.permission.cts/files" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="CtsAppThatRequestsPermissionAandB.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsPermissionAandB.apk" />
+ <option name="push" value="CtsAppThatRequestsPermissionAandC.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsPermissionAandC.apk" />
+ <option name="push" value="CtsAppThatRequestsBluetoothPermission30.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsBluetoothPermission30.apk" />
+ <option name="push" value="CtsAppThatRequestsBluetoothPermission31.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsBluetoothPermission31.apk" />
+ <option name="push" value="CtsAppThatRequestsBluetoothPermissionNeverForLocation31.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsBluetoothPermissionNeverForLocation31.apk" />
+ <option name="push" value="CtsAppThatRequestsBluetoothPermissionNeverForLocationNoProvider.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsBluetoothPermissionNeverForLocationNoProvider.apk" />
+ <option name="push" value="CtsAppThatRequestsContactsPermission16.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsContactsPermission16.apk" />
+ <option name="push" value="CtsAppThatRequestsContactsPermission15.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsContactsPermission15.apk" />
+ <option name="push" value="CtsAppThatRequestsContactsAndCallLogPermission16.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsContactsAndCallLogPermission16.apk" />
+ <option name="push" value="CtsAppThatRequestsLocationPermission29.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission29.apk" />
+ <option name="push" value="CtsAppThatRequestsLocationPermission29v4.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission29v4.apk" />
+ <option name="push" value="CtsAppThatRequestsLocationPermission28.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission28.apk" />
+ <option name="push" value="CtsAppThatRequestsLocationPermission22.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationPermission22.apk" />
+ <option name="push" value="CtsAppThatRequestsStoragePermission22.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsStoragePermission22.apk" />
+ <option name="push" value="CtsAppThatRequestsStoragePermission29.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsStoragePermission29.apk" />
+ <option name="push" value="CtsAppThatRequestsStoragePermission28.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsStoragePermission28.apk" />
+ <option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission28.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationAndBackgroundPermission28.apk" />
+ <option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission29.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsLocationAndBackgroundPermission29.apk" />
+ <option name="push" value="CtsAppThatAccessesLocationOnCommand.apk->/data/local/tmp/cts-permission/CtsAppThatAccessesLocationOnCommand.apk" />
+ <option name="push" value="AppThatDoesNotHaveBgLocationAccess.apk->/data/local/tmp/cts-permission/AppThatDoesNotHaveBgLocationAccess.apk" />
+ <option name="push" value="CtsAppWithSharedUidThatRequestsPermissions.apk->/data/local/tmp/cts-permission/CtsAppWithSharedUidThatRequestsPermissions.apk" />
+ <option name="push" value="CtsAppWithSharedUidThatRequestsNoPermissions.apk->/data/local/tmp/cts-permission/CtsAppWithSharedUidThatRequestsNoPermissions.apk" />
+ <option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission28.apk->/data/local/tmp/cts-permission/CtsAppWithSharedUidThatRequestsLocationPermission28.apk" />
+ <option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission29.apk->/data/local/tmp/cts-permission/CtsAppWithSharedUidThatRequestsLocationPermission29.apk" />
+ <option name="push" value="CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk" />
+ <option name="push" value="CtsAppThatRunsRationaleTests.apk->/data/local/tmp/cts-permission/CtsAppThatRunsRationaleTests.apk" />
+ <option name="push" value="CtsAdversarialPermissionUserApp.apk->/data/local/tmp/cts-permission/CtsAdversarialPermissionUserApp.apk" />
+ <option name="push" value="CtsAdversarialPermissionDefinerApp.apk->/data/local/tmp/cts-permission/CtsAdversarialPermissionDefinerApp.apk" />
+ <option name="push" value="CtsVictimPermissionDefinerApp.apk->/data/local/tmp/cts-permission/CtsVictimPermissionDefinerApp.apk" />
+ <option name="push" value="CtsRuntimePermissionDefinerApp.apk->/data/local/tmp/cts-permission/CtsRuntimePermissionDefinerApp.apk" />
+ <option name="push" value="CtsRuntimePermissionUserApp.apk->/data/local/tmp/cts-permission/CtsRuntimePermissionUserApp.apk" />
+ <option name="push" value="CtsInstallPermissionDefinerApp.apk->/data/local/tmp/cts-permission/CtsInstallPermissionDefinerApp.apk" />
+ <option name="push" value="CtsInstallPermissionUserApp.apk->/data/local/tmp/cts-permission/CtsInstallPermissionUserApp.apk" />
+ <option name="push" value="CtsInstallPermissionEscalatorApp.apk->/data/local/tmp/cts-permission/CtsInstallPermissionEscalatorApp.apk" />
+ <option name="push" value="CtsAppThatRequestsOneTimePermission.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsOneTimePermission.apk" />
+ <option name="push" value="CtsAppToTestRevokeSelfPermission.apk->/data/local/tmp/cts-permission/CtsAppToTestRevokeSelfPermission.apk" />
+ <option name="push" value="AppThatDefinesUndefinedPermissionGroupElement.apk->/data/local/tmp/cts-permission/AppThatDefinesUndefinedPermissionGroupElement.apk" />
+ <option name="push" value="CtsAppThatDefinesPermissionA.apk->/data/local/tmp/cts-permission/CtsAppThatDefinesPermissionA.apk" />
+ <option name="push" value="CtsAppThatAlsoDefinesPermissionA.apk->/data/local/tmp/cts-permission/CtsAppThatAlsoDefinesPermissionA.apk" />
+ <option name="push" value="CtsAppThatAlsoDefinesPermissionADifferentCert.apk->/data/local/tmp/cts-permission/CtsAppThatAlsoDefinesPermissionADifferentCert.apk" />
+ <option name="push" value="CtsAppThatAlsoDefinesPermissionGroupADifferentCert.apk->/data/local/tmp/cts-permission/CtsAppThatAlsoDefinesPermissionGroupADifferentCert.apk" />
+ <option name="push" value="CtsAppThatDefinesPermissionInPlatformGroup.apk->/data/local/tmp/cts-permission/CtsAppThatDefinesPermissionInPlatformGroup.apk" />
+ <option name="push" value="CtsAppThatAlsoDefinesPermissionGroupADifferentCert30.apk->/data/local/tmp/cts-permission/CtsAppThatAlsoDefinesPermissionGroupADifferentCert30.apk" />
+ <option name="push" value="CtsAppThatDefinesPermissionWithInvalidGroup.apk->/data/local/tmp/cts-permission/CtsAppThatDefinesPermissionWithInvalidGroup.apk" />
+ <option name="push" value="CtsAppThatDefinesPermissionWithInvalidGroup30.apk->/data/local/tmp/cts-permission/CtsAppThatDefinesPermissionWithInvalidGroup30.apk" />
+ <option name="push" value="CtsStorageEscalationApp28.apk->/data/local/tmp/cts-permission/CtsStorageEscalationApp28.apk" />
+ <option name="push" value="CtsStorageEscalationApp29Full.apk->/data/local/tmp/cts-permission/CtsStorageEscalationApp29Full.apk" />
+ <option name="push" value="CtsStorageEscalationApp29Scoped.apk->/data/local/tmp/cts-permission/CtsStorageEscalationApp29Scoped.apk" />
+ <option name="push" value="CtsAppThatHasNotificationListener.apk->/data/local/tmp/cts-permission/CtsAppThatHasNotificationListener.apk" />
+ <option name="push" value="CtsAppThatRequestsMultiplePermissionsWithMinMaxSdk.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsMultiplePermissionsWithMinMaxSdk.apk" />
+ <option name="push" value="CtsAppThatRequestsSystemAlertWindow22.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsSystemAlertWindow22.apk" />
+ <option name="push" value="CtsAppThatRequestsSystemAlertWindow23.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsSystemAlertWindow23.apk" />
+ <option name="push" value="CtsAppThatRequestCustomCameraPermission.apk->/data/local/tmp/cts-permission/CtsAppThatRequestCustomCameraPermission.apk" />
+ <option name="push" value="CtsAppThatRequestsDevicePermissions.apk->/data/local/tmp/cts-permission/CtsAppThatRequestsDevicePermissions.apk" />
+ </target_preparer>
+
+ <!-- Remove additional apps if installed -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- disable DeprecatedAbi warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <!-- disable DeprecatedTargetSdk warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestpermission" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestnopermission" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.AdversarialPermissionDefinerApp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.VictimPermissionDefinerApp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.userapp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.appthathasnotificationlistener" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permission.cts" />
+ <option name="runtime-hint" value="13m" />
+ </test>
+
+ <system_checker class="com.android.tradefed.suite.checker.UserChecker" >
+ <option name="user-cleanup" value="true" />
+ </system_checker>
+</configuration>
diff --git a/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp b/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp
new file mode 100644
index 000000000..fdb0be452
--- /dev/null
+++ b/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml b/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
new file mode 100644
index 000000000..ece3ba1c7
--- /dev/null
+++ b/tests/cts/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permission.cts.appthatrequestcustompermission"
+ android:versionCode="1">
+
+ <permission-group
+ android:name="android.permission.cts.appthatrequestcustompermission.TEST_GROUP"
+ android:label="test permission group"
+ android:protectionLevel="dangerous" />
+
+ <permission
+ android:name="android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION"
+ android:label="test permission"
+ android:permissionGroup="android.permission.cts.appthatrequestcustompermission.TEST_GROUP"
+ android:protectionLevel="dangerous" />
+
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+ <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION" />
+
+ <application />
+</manifest>
diff --git a/tests/cts/permission/AppThatAccessesLocationOnCommand/Android.bp b/tests/cts/permission/AppThatAccessesLocationOnCommand/Android.bp
new file mode 100644
index 000000000..2bb3dd3ab
--- /dev/null
+++ b/tests/cts/permission/AppThatAccessesLocationOnCommand/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatAccessesLocationOnCommand",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl"
+ ],
+}
diff --git a/tests/cts/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml b/tests/cts/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml
new file mode 100644
index 000000000..93836d389
--- /dev/null
+++ b/tests/cts/permission/AppThatAccessesLocationOnCommand/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.permission.cts.appthataccesseslocation">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+
+ <application android:label="CtsLocationAccess" android:debuggable="true">
+ <service android:name=".AccessLocationOnCommand"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java b/tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
new file mode 100644
index 000000000..75f4a0ce5
--- /dev/null
+++ b/tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthataccesseslocation;
+
+import static android.location.Criteria.ACCURACY_FINE;
+
+import android.app.Service;
+import android.content.Intent;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+
+public class AccessLocationOnCommand extends Service {
+ private IAccessLocationOnCommand.Stub mBinder = new IAccessLocationOnCommand.Stub() {
+ public void accessLocation() {
+ Criteria crit = new Criteria();
+ crit.setAccuracy(ACCURACY_FINE);
+
+ AccessLocationOnCommand.this.getSystemService(LocationManager.class)
+ .requestSingleUpdate(crit, new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status,
+ Bundle extras) {
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+ }, Looper.getMainLooper());
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return true;
+ }
+}
diff --git a/tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl b/tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
new file mode 100644
index 000000000..be92ed160
--- /dev/null
+++ b/tests/cts/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts.appthataccesseslocation;
+
+interface IAccessLocationOnCommand {
+ /** Access location on command */
+ void accessLocation();
+} \ No newline at end of file
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionA/Android.bp b/tests/cts/permission/AppThatAlsoDefinesPermissionA/Android.bp
new file mode 100644
index 000000000..46b7aecd3
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionA/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatAlsoDefinesPermissionA",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ certificate: ":cts-testkey1",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionA/AndroidManifest.xml b/tests/cts/permission/AppThatAlsoDefinesPermissionA/AndroidManifest.xml
new file mode 100644
index 000000000..2a803017b
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionA/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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="android.permission.cts.appthatalsodefinespermissiona">
+
+ <permission android:name="com.android.cts.duplicatepermission.permA"
+ android:permissionGroup="com.android.cts.duplicatepermission.groupA"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp b/tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp
new file mode 100644
index 000000000..c88d0f7fe
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatAlsoDefinesPermissionADifferentCert",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ certificate: ":cts-testkey2",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/AndroidManifest.xml b/tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/AndroidManifest.xml
new file mode 100644
index 000000000..d333bf6df
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionADifferentCert/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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="android.permission.cts.appthatdefinespermissiona.differentcert">
+
+ <permission android:name="com.android.cts.duplicatepermission.permA"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp
new file mode 100644
index 000000000..b1ef695ac
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatAlsoDefinesPermissionGroupADifferentCert",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ certificate: ":cts-testkey2",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/AndroidManifest.xml b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/AndroidManifest.xml
new file mode 100644
index 000000000..59cd518c1
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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="android.permission.cts.appthatdefinespermissiongroupa.differentcert">
+
+ <permission-group android:name="com.android.cts.duplicatepermission.groupA"
+ android:label="groupA"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp
new file mode 100644
index 000000000..52900dfc3
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatAlsoDefinesPermissionGroupADifferentCert30",
+ defaults: ["cts_defaults"],
+ sdk_version: "30",
+ certificate: ":cts-testkey2",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/AndroidManifest.xml b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/AndroidManifest.xml
new file mode 100644
index 000000000..43ed9db58
--- /dev/null
+++ b/tests/cts/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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="android.permission.cts.appthatdefinespermissiongroupa.differentcert30">
+
+ <permission-group android:name="com.android.cts.duplicatepermission.groupA"
+ android:label="groupA"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatDefinesPermissionA/Android.bp b/tests/cts/permission/AppThatDefinesPermissionA/Android.bp
new file mode 100644
index 000000000..54a575b4f
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionA/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatDefinesPermissionA",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ certificate: ":cts-testkey1",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatDefinesPermissionA/AndroidManifest.xml b/tests/cts/permission/AppThatDefinesPermissionA/AndroidManifest.xml
new file mode 100644
index 000000000..527618c7d
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionA/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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="android.permission.cts.appthatdefinespermissiona">
+ <permission-group android:name="com.android.cts.duplicatepermission.groupA"
+ android:label="groupA"/>
+
+ <permission android:name="com.android.cts.duplicatepermission.permA"
+ android:permissionGroup="com.android.cts.duplicatepermission.groupA"/>
+
+ <application/>
+</manifest>
+
diff --git a/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp
new file mode 100644
index 000000000..9029b3a98
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatDefinesPermissionWithInvalidGroup",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/AndroidManifest.xml b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/AndroidManifest.xml
new file mode 100644
index 000000000..8abd4cc4a
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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="android.permission.cts.appthatdefinespermissionwithinvalidgroup">
+
+ <permission android:name="com.android.cts.duplicatepermission.permA"
+ android:permissionGroup="com.android.cts.duplicatepermission.invalid"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp
new file mode 100644
index 000000000..04961e265
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatDefinesPermissionWithInvalidGroup30",
+ defaults: ["cts_defaults"],
+ sdk_version: "30",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/AndroidManifest.xml b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/AndroidManifest.xml
new file mode 100644
index 000000000..2fc662c27
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionWithInvalidGroup30/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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="android.permission.cts.appthatdefinespermissionwithinvalidgroup30">
+
+ <permission android:name="com.android.cts.duplicatepermission.permA"
+ android:permissionGroup="com.android.cts.duplicatepermission.invalid"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp b/tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp
new file mode 100644
index 000000000..687ad7488
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatDefinesPermissionInPlatformGroup",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+}
diff --git a/tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/AndroidManifest.xml b/tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/AndroidManifest.xml
new file mode 100644
index 000000000..d4709eb9b
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesPermissionWithPlatformGroup/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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="android.permission.cts.appthatdefinespermissioninplatformgroup">
+
+ <permission android:name="com.android.cts.duplicatepermission.permA"
+ android:permissionGroup="android.permission-group.CAMERA"/>
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
new file mode 100644
index 000000000..c00d26d3a
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "AppThatDefinesUndefinedPermissionGroupElement",
+ defaults: ["cts_defaults"],
+ sdk_version: "test_current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+ srcs: ["src/**/*.kt"],
+}
diff --git a/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml
new file mode 100644
index 000000000..ab2ef79b2
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?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="android.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <permission
+ android:name="android.permission.cts.appthatrequestpermission.TEST"
+ android:protectionLevel="dangerous"
+ android:permissionGroup="android.permission-group.UNDEFINED" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.TEST" />
+
+ <application android:label="CtsPermissionUnknownGroup">
+ <activity
+ android:name=".RequestPermissions"
+ android:exported="true"
+ android:visibleToInstantApps="true"/>
+ </application>
+</manifest>
+
diff --git a/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
new file mode 100644
index 000000000..bbf9066f2
--- /dev/null
+++ b/tests/cts/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+package android.permission.cts.appthatrequestpermission
+
+import android.app.Activity
+import android.os.Bundle
+import android.util.Log
+
+class RequestPermissions : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+ return
+ }
+
+ val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS)!!
+ requestPermissions(permissions, 0)
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<String>,
+ grantResults: IntArray
+ ) {
+ finish()
+ }
+
+ companion object {
+ private const val EXTRA_PERMISSIONS =
+ "android.permission.cts.appthatrequestpermission.extra.PERMISSIONS"
+ private val TAG = RequestPermissions::class.simpleName
+ }
+}
diff --git a/tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp b/tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp
new file mode 100644
index 000000000..f9ff21c6f
--- /dev/null
+++ b/tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "AppThatDoesNotHaveBgLocationAccess",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml b/tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml
new file mode 100644
index 000000000..8e883a4ad
--- /dev/null
+++ b/tests/cts/permission/AppThatDoesNotHaveBgLocationAccess/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.permission.cts.appthataccesseslocation">
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <application android:label="CtsLocationAccess" android:debuggable="true"/>
+</manifest>
+
diff --git a/tests/cts/permission/AppThatHasNotificationListener/Android.bp b/tests/cts/permission/AppThatHasNotificationListener/Android.bp
new file mode 100644
index 000000000..419ab5d66
--- /dev/null
+++ b/tests/cts/permission/AppThatHasNotificationListener/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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_helper_app {
+ name: "CtsAppThatHasNotificationListener",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ sdk_version: "current",
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+}
diff --git a/tests/cts/permission/AppThatHasNotificationListener/AndroidManifest.xml b/tests/cts/permission/AppThatHasNotificationListener/AndroidManifest.xml
new file mode 100644
index 000000000..03d23dfb2
--- /dev/null
+++ b/tests/cts/permission/AppThatHasNotificationListener/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.permission.cts.appthathasnotificationlistener"
+ android:versionCode="1">
+
+ <application android:label="CtsNotificationListener">
+ <service
+ android:name=".CtsNotificationListenerService"
+ android:label="CtsNotificationListener"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatHasNotificationListener/src/android/permission/cts/appthathasnotificationlistener/CtsNotificationListenerService.java b/tests/cts/permission/AppThatHasNotificationListener/src/android/permission/cts/appthathasnotificationlistener/CtsNotificationListenerService.java
new file mode 100644
index 000000000..2bd423e1b
--- /dev/null
+++ b/tests/cts/permission/AppThatHasNotificationListener/src/android/permission/cts/appthathasnotificationlistener/CtsNotificationListenerService.java
@@ -0,0 +1,21 @@
+/*
+ * 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 android.permission.cts.appthathasnotificationlistener;
+
+import android.service.notification.NotificationListenerService;
+
+public class CtsNotificationListenerService extends NotificationListenerService {}
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermission30/Android.bp b/tests/cts/permission/AppThatRequestBluetoothPermission30/Android.bp
new file mode 100644
index 000000000..a3fe38109
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermission30/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2021 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"],
+}
+
+filegroup {
+ name: "AppThatRequestBluetoothPermission",
+ srcs: [
+ "src/**/*.java",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatRequestsBluetoothPermission30",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: [":AppThatRequestBluetoothPermission"],
+}
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermission30/AndroidManifest.xml b/tests/cts/permission/AppThatRequestBluetoothPermission30/AndroidManifest.xml
new file mode 100644
index 000000000..d84e0d8f4
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermission30/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 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.permission.cts.appthatrequestpermission">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+
+ <application>
+ <provider
+ android:name=".AccessBluetoothOnCommand"
+ android:authorities="appthatrequestpermission"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermission30/src/android/permission/cts/appthatrequestpermission/AccessBluetoothOnCommand.java b/tests/cts/permission/AppThatRequestBluetoothPermission30/src/android/permission/cts/appthatrequestpermission/AccessBluetoothOnCommand.java
new file mode 100644
index 000000000..a27da0bdc
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermission30/src/android/permission/cts/appthatrequestpermission/AccessBluetoothOnCommand.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrequestpermission;
+
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanResult;
+import android.content.AttributionSource;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContextParams;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Base64;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AccessBluetoothOnCommand extends ContentProvider {
+ private static final String TAG = "AccessBluetoothOnCommand";
+ private static final String DISAVOWAL_APP_PKG = "android.permission.cts.appneverforlocation";
+
+ private enum Result {
+ UNKNOWN, EXCEPTION, EMPTY, FILTERED, FULL
+ }
+
+ @Override
+ public Bundle call(String authority, String method, String arg, Bundle extras) {
+ final Bundle res = new Bundle();
+
+ BluetoothLeScanner scanner = null;
+ ScanCallback scanCallback = null;
+
+ try {
+ Context context = ("PROXY".equals(arg)) ? createProxyingContext() : getContext();
+ scanner = context.getSystemService(BluetoothManager.class)
+ .getAdapter().getBluetoothLeScanner();
+
+ final Set<String> observedScans = ConcurrentHashMap.newKeySet();
+ final AtomicInteger observedErrorCode = new AtomicInteger(0);
+
+ scanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ Log.v(TAG, "onScanResult() - result = " + result);
+ observedScans.add(Base64.encodeToString(result.getScanRecord().getBytes(), 0));
+ }
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ for (ScanResult result : results) {
+ onScanResult(0, result);
+ }
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ Log.v(TAG, "onScanFailed() - errorCode = " + errorCode);
+ observedErrorCode.set(errorCode);
+ }
+ };
+
+ scanner.startScan(scanCallback);
+
+ // Wait a few seconds to figure out what we actually observed
+ SystemClock.sleep(3000);
+
+ if (observedErrorCode.get() > 0) {
+ throw new RuntimeException("Scan returned error code: " + observedErrorCode.get());
+ }
+
+ switch (observedScans.size()) {
+ case 0:
+ res.putInt(Intent.EXTRA_INDEX, Result.EMPTY.ordinal());
+ break;
+ case 1:
+ res.putInt(Intent.EXTRA_INDEX, Result.FILTERED.ordinal());
+ break;
+ case 5:
+ res.putInt(Intent.EXTRA_INDEX, Result.FULL.ordinal());
+ break;
+ default:
+ res.putInt(Intent.EXTRA_INDEX, Result.UNKNOWN.ordinal());
+ break;
+ }
+ } catch (Throwable t) {
+ Log.v(TAG, "Failed to scan", t);
+ res.putInt(Intent.EXTRA_INDEX, Result.EXCEPTION.ordinal());
+ } finally {
+ try {
+ scanner.stopScan(scanCallback);
+ } catch (Exception e) {
+ }
+ }
+ return res;
+ }
+
+ private Context createProxyingContext() throws PackageManager.NameNotFoundException {
+ int disavowingAppUid =
+ getContext().getPackageManager().getPackageUid(DISAVOWAL_APP_PKG, 0);
+ AttributionSource attrib = new AttributionSource.Builder(disavowingAppUid)
+ .setPackageName(DISAVOWAL_APP_PKG)
+ .build();
+ return getContext().createContext(
+ new ContextParams.Builder().setNextAttributionSource(attrib).build());
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermission31/Android.bp b/tests/cts/permission/AppThatRequestBluetoothPermission31/Android.bp
new file mode 100644
index 000000000..7dc2e2445
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermission31/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsAppThatRequestsBluetoothPermission31",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: [":AppThatRequestBluetoothPermission"],
+}
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermission31/AndroidManifest.xml b/tests/cts/permission/AppThatRequestBluetoothPermission31/AndroidManifest.xml
new file mode 100644
index 000000000..70b381170
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermission31/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 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.permission.cts.appthatrequestpermission">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
+
+ <application>
+ <provider
+ android:name=".AccessBluetoothOnCommand"
+ android:authorities="appthatrequestpermission"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp
new file mode 100644
index 000000000..857f2e2f4
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsAppThatRequestsBluetoothPermissionNeverForLocation31",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: [":AppThatRequestBluetoothPermission"],
+}
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/AndroidManifest.xml b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/AndroidManifest.xml
new file mode 100644
index 000000000..446933d21
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocation31/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 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.permission.cts.appthatrequestpermission">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <uses-permission
+ android:name="android.permission.BLUETOOTH_ADVERTISE"
+ android:usesPermissionFlags="neverForLocation" />
+ <uses-permission
+ android:name="android.permission.BLUETOOTH_CONNECT"
+ android:usesPermissionFlags="neverForLocation" />
+ <uses-permission
+ android:name="android.permission.BLUETOOTH_SCAN"
+ android:usesPermissionFlags="neverForLocation" />
+
+ <application>
+ <provider
+ android:name=".AccessBluetoothOnCommand"
+ android:authorities="appthatrequestpermission"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp
new file mode 100644
index 000000000..6f635c0cc
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsAppThatRequestsBluetoothPermissionNeverForLocationNoProvider",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/AndroidManifest.xml b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/AndroidManifest.xml
new file mode 100644
index 000000000..6b4a991be
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 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.permission.cts.appneverforlocation">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <uses-permission
+ android:name="android.permission.BLUETOOTH_ADVERTISE"
+ android:usesPermissionFlags="neverForLocation" />
+ <uses-permission
+ android:name="android.permission.BLUETOOTH_CONNECT"
+ android:usesPermissionFlags="neverForLocation" />
+ <uses-permission
+ android:name="android.permission.BLUETOOTH_SCAN"
+ android:usesPermissionFlags="neverForLocation" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp b/tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp
new file mode 100644
index 000000000..1a057d010
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsContactsAndCallLogPermission16",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/AndroidManifest.xml b/tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/AndroidManifest.xml
new file mode 100644
index 000000000..08f014508
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestContactsAndCallLogPermission16/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="3">
+
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestContactsPermission15/Android.bp b/tests/cts/permission/AppThatRequestContactsPermission15/Android.bp
new file mode 100644
index 000000000..54618447e
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestContactsPermission15/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsContactsPermission15",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestContactsPermission15/AndroidManifest.xml b/tests/cts/permission/AppThatRequestContactsPermission15/AndroidManifest.xml
new file mode 100644
index 000000000..ab17c3668
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestContactsPermission15/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestContactsPermission16/Android.bp b/tests/cts/permission/AppThatRequestContactsPermission16/Android.bp
new file mode 100644
index 000000000..2dca5aa2c
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestContactsPermission16/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsContactsPermission16",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestContactsPermission16/AndroidManifest.xml b/tests/cts/permission/AppThatRequestContactsPermission16/AndroidManifest.xml
new file mode 100644
index 000000000..703bb3a75
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestContactsPermission16/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestCustomCameraPermission/Android.bp b/tests/cts/permission/AppThatRequestCustomCameraPermission/Android.bp
new file mode 100644
index 000000000..61a0e902f
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestCustomCameraPermission/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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_helper_app {
+ name: "CtsAppThatRequestCustomCameraPermission",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "mts",
+ "sts",
+ "general-tests",
+ ],
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppThatRequestCustomCameraPermission/AndroidManifest.xml b/tests/cts/permission/AppThatRequestCustomCameraPermission/AndroidManifest.xml
new file mode 100644
index 000000000..a8143a78e
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestCustomCameraPermission/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?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.permission.cts.appthatrequestcustomcamerapermission">
+
+ <permission android:name="appthatrequestcustomcamerapermission.CUSTOM"
+ android:permissionGroup="android.permission-group.CAMERA"
+ android:label="@string/permlab_custom"
+ android:description="@string/permdesc_custom"
+ android:protectionLevel="dangerous" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="appthatrequestcustomcamerapermission.CUSTOM" />
+
+ <application>
+ <activity android:name=".RequestCameraPermission" android:exported="true"
+ android:visibleToInstantApps="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestCustomCameraPermission/res/values/strings.xml b/tests/cts/permission/AppThatRequestCustomCameraPermission/res/values/strings.xml
new file mode 100644
index 000000000..8de46384b
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestCustomCameraPermission/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ * 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>
+ <string name="permlab_custom">Custom</string>
+ <string name="permdesc_custom">allows bypassing one-time permissions</string>
+</resources> \ No newline at end of file
diff --git a/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java b/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java
new file mode 100644
index 000000000..288e7e1b3
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestCustomCameraPermission/src/android/permission/cts/appthatrequestcustomcamerapermission/RequestCameraPermission.java
@@ -0,0 +1,85 @@
+/*
+ * 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 android.permission.cts.appthatrequestcustomcamerapermission;
+
+import static android.Manifest.permission.CAMERA;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+
+public class RequestCameraPermission extends Activity {
+ private static final String LOG_TAG = RequestCameraPermission.class.getSimpleName();
+
+ public static final String CUSTOM_PERMISSION = "appthatrequestcustomcamerapermission.CUSTOM";
+ private Handler mHandler;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ Log.w(LOG_TAG, "Activity was recreated. (Perhaps due to a configuration change?)");
+ return;
+ }
+
+ boolean cameraGranted =
+ checkSelfPermission(CAMERA) == PERMISSION_GRANTED;
+ boolean customGranted =
+ checkSelfPermission(CUSTOM_PERMISSION) == PERMISSION_GRANTED;
+
+ mHandler = new Handler(getMainLooper());
+
+ if (!cameraGranted && !customGranted) {
+ requestPermissions(new String[] {CAMERA}, 0);
+ } else {
+ Log.e(LOG_TAG, "Test app was opened with cameraGranted=" + cameraGranted
+ + " and customGranted=" + customGranted);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+ if (requestCode == 0) {
+ if (grantResults[0] != PERMISSION_GRANTED) {
+ Log.e(LOG_TAG, "permission wasn't granted, this test should fail,"
+ + " leaving test app open.");
+ } else {
+ // Delayed request because the immediate request might show the dialog again
+ mHandler.postDelayed(() ->
+ requestPermissions(new String[] {CUSTOM_PERMISSION}, 1), 500);
+ }
+ } else if (requestCode == 1) {
+ if (grantResults[0] != PERMISSION_GRANTED) {
+ Log.e(LOG_TAG, "permission wasn't granted, this test should fail,"
+ + " leaving test app open.");
+ } else {
+ // Here camera was granted and custom was autogranted, exit process and let test
+ // verify both are revoked.
+
+ // Delayed exit because b/254675301
+ mHandler.postDelayed(() -> System.exit(0), 1000);
+ }
+ }
+
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestDevicePermissions/Android.bp b/tests/cts/permission/AppThatRequestDevicePermissions/Android.bp
new file mode 100644
index 000000000..c0a9f9914
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestDevicePermissions/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatRequestsDevicePermissions",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ srcs: ["src/**/*.kt"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestDevicePermissions/AndroidManifest.xml b/tests/cts/permission/AppThatRequestDevicePermissions/AndroidManifest.xml
new file mode 100644
index 000000000..a96342706
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestDevicePermissions/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <permission android:name="android.permission.cts.CUSTOM_SIGNATURE_PERMISSION"
+ android:protectionLevel="signature"/>
+ <uses-permission android:name="android.permission.cts.CUSTOM_SIGNATURE_PERMISSION" />
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <application>
+ <receiver
+ android:name="android.permission.cts.appthatrequestpermission.RevokeSelfPermissionReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.permission.cts.appthatrequestpermission.REVOKE_SELF_PERMISSION" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestDevicePermissions/src/android/permission/cts/appthatrequestpermission/RevokeSelfPermissionReceiver.kt b/tests/cts/permission/AppThatRequestDevicePermissions/src/android/permission/cts/appthatrequestpermission/RevokeSelfPermissionReceiver.kt
new file mode 100644
index 000000000..4ce6a2aaa
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestDevicePermissions/src/android/permission/cts/appthatrequestpermission/RevokeSelfPermissionReceiver.kt
@@ -0,0 +1,37 @@
+/*
+ * 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 android.permission.cts.appthatrequestpermission
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Handler
+import android.os.Process
+
+/** Revokes permission for a device provided in the intent. */
+class RevokeSelfPermissionReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val permissionName = intent.getStringExtra("permissionName")!!
+ val deviceId = intent.getIntExtra("deviceID", Context.DEVICE_ID_INVALID)
+ val deviceContext = context.createDeviceContext(deviceId)
+ deviceContext.revokeSelfPermissionOnKill(permissionName)
+
+ // revokeSelfPermissionOnKill is an async API, and the work is executed by main
+ // thread, so we add the kill to the queue to be executed after revoke call.
+ val handler = Handler.createAsync(context.mainLooper)
+ handler.postDelayed({ Process.killProcess(Process.myPid()) }, 1000)
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp
new file mode 100644
index 000000000..5a60a3fbd
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatRequestsLocationAndBackgroundPermission28",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/AndroidManifest.xml
new file mode 100644
index 000000000..626ee3d43
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission28/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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="android.permission.cts.appthatrequestpermission"
+ android:versionCode="3">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <!-- The ACCESS_BACKGROUND_LOCATION was added for API 29. But apps targeting lower APK levels
+ can still request it to signal that they are aware of this new behavior -->
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp
new file mode 100644
index 000000000..de6c3cbb2
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsLocationAndBackgroundPermission29",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ min_sdk_version: "29",
+ target_sdk_version: "29",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/AndroidManifest.xml
new file mode 100644
index 000000000..285502cb2
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationAndBackgroundPermission29/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="3">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestLocationPermission22/Android.bp b/tests/cts/permission/AppThatRequestLocationPermission22/Android.bp
new file mode 100644
index 000000000..25d9893ec
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission22/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsLocationPermission22",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestLocationPermission22/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationPermission22/AndroidManifest.xml
new file mode 100644
index 000000000..78251baea
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission22/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestLocationPermission28/Android.bp b/tests/cts/permission/AppThatRequestLocationPermission28/Android.bp
new file mode 100644
index 000000000..bfeadbd58
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission28/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsLocationPermission28",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestLocationPermission28/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationPermission28/AndroidManifest.xml
new file mode 100644
index 000000000..c8cf95761
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission28/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestLocationPermission29/Android.bp b/tests/cts/permission/AppThatRequestLocationPermission29/Android.bp
new file mode 100644
index 000000000..dd320a77f
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission29/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsLocationPermission29",
+ defaults: ["cts_defaults"],
+ min_sdk_version: "29",
+ target_sdk_version: "29",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppThatRequestLocationPermission29/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationPermission29/AndroidManifest.xml
new file mode 100644
index 000000000..17a0e0d35
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission29/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestLocationPermission29v4/Android.bp b/tests/cts/permission/AppThatRequestLocationPermission29v4/Android.bp
new file mode 100644
index 000000000..721f71534
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission29v4/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppThatRequestsLocationPermission29v4",
+ defaults: ["cts_defaults"],
+ min_sdk_version: "29",
+ target_sdk_version: "29",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml b/tests/cts/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml
new file mode 100644
index 000000000..572222c49
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestLocationPermission29v4/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permission.cts.appthatrequestpermission"
+ android:versionCode="4">
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/Android.bp b/tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/Android.bp
new file mode 100644
index 000000000..5fcc6c8ba
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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_helper_app {
+ name: "CtsAppThatRequestsMultiplePermissionsWithMinMaxSdk",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/AndroidManifest.xml b/tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/AndroidManifest.xml
new file mode 100644
index 000000000..e75a7f5c9
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestMultiplePermissionsWithMinMaxSdk/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.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.permissions.MINSDK_LT_DEVICESDK" android:minSdkVersion="32" />
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.permissions.MINSDK_GT_DEVICESDK" android:minSdkVersion="2147483647" />
+
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.permissions.MAXSDK_LT_DEVICESDK" android:maxSdkVersion="32" />
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.permissions.MAXSDK_GT_DEVICESDK" android:maxSdkVersion="2147483647" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestOneTimePermission/Android.bp b/tests/cts/permission/AppThatRequestOneTimePermission/Android.bp
new file mode 100644
index 000000000..d9c2f76c2
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestOneTimePermission/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppThatRequestsOneTimePermission",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "mts-permission",
+ "general-tests",
+ ],
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppThatRequestOneTimePermission/AndroidManifest.xml b/tests/cts/permission/AppThatRequestOneTimePermission/AndroidManifest.xml
new file mode 100644
index 000000000..24fc537cf
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestOneTimePermission/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.appthatrequestpermission">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
+
+ <application>
+ <activity android:name=".RequestPermission" android:exported="true"
+ android:visibleToInstantApps="true" />
+ <service android:name=".KeepAliveForegroundService"
+ android:foregroundServiceType="specialUse"
+ android:exported="true"
+ android:visibleToInstantApps="true" >
+ <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" android:value="cts" />
+ </service>
+ </application>
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/KeepAliveForegroundService.java b/tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/KeepAliveForegroundService.java
new file mode 100644
index 000000000..e41a47321
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/KeepAliveForegroundService.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrequestpermission;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+
+public class KeepAliveForegroundService extends Service {
+
+ private static final String EXTRA_FOREGROUND_SERVICE_LIFESPAN =
+ "android.permission.cts.OneTimePermissionTest.EXTRA_FOREGROUND_SERVICE_LIFESPAN";
+
+ private static final String EXTRA_FOREGROUND_SERVICE_STICKY =
+ "android.permission.cts.OneTimePermissionTest.EXTRA_FOREGROUND_SERVICE_STICKY";
+
+ private static final String CHANNEL_ID = "channelId";
+ private static final String CHANNEL_NAME = "channelName";
+
+ private static final long DEFAULT_LIFESPAN = 5000;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ long lifespan;
+ boolean sticky;
+ if (intent == null) {
+ lifespan = DEFAULT_LIFESPAN;
+ sticky = false;
+ } else {
+ lifespan = intent.getLongExtra(EXTRA_FOREGROUND_SERVICE_LIFESPAN, DEFAULT_LIFESPAN);
+ sticky = intent.getBooleanExtra(EXTRA_FOREGROUND_SERVICE_STICKY, false);
+ }
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(
+ new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_LOW));
+ Notification notification = new Notification.Builder(this, CHANNEL_ID)
+ .setSmallIcon(android.R.drawable.ic_lock_lock)
+ .build();
+ startForeground(1, notification);
+ new Handler(Looper.getMainLooper()).postDelayed(
+ () -> stopForeground(Service.STOP_FOREGROUND_REMOVE), lifespan);
+ if (sticky) {
+ return START_STICKY;
+ }
+ return super.onStartCommand(intent, flags, startId);
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/RequestPermission.java b/tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/RequestPermission.java
new file mode 100644
index 000000000..f2910c391
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestOneTimePermission/src/android/permission/cts/appthatrequestpermission/RequestPermission.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrequestpermission;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class RequestPermission extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestPermissions(new String[] {"android.permission.ACCESS_FINE_LOCATION"}, 0);
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestPermissionAandB/Android.bp b/tests/cts/permission/AppThatRequestPermissionAandB/Android.bp
new file mode 100644
index 000000000..6c037b456
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandB/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsPermissionAandB",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: ["src/**/*.java"],
+}
diff --git a/tests/cts/permission/AppThatRequestPermissionAandB/AndroidManifest.xml b/tests/cts/permission/AppThatRequestPermissionAandB/AndroidManifest.xml
new file mode 100644
index 000000000..4c85a262b
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandB/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission">
+
+ <permission android:name="android.permission.cts.appthatrequestpermission.A"
+ android:protectionLevel="dangerous"
+ android:label="@string/perm_a"
+ android:permissionGroup="android.permission.cts.groupB"
+ android:description="@string/perm_a" />
+
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.A" />
+ <uses-permission android:name="android.permission.cts.B" />
+
+ <application>
+ <activity android:name=".RequestPermission" android:exported="true"
+ android:visibleToInstantApps="true" />
+ </application>
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestPermissionAandB/res/values/strings.xml b/tests/cts/permission/AppThatRequestPermissionAandB/res/values/strings.xml
new file mode 100644
index 000000000..563009789
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandB/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="perm_a">Permission A</string>
+</resources>
diff --git a/tests/cts/permission/AppThatRequestPermissionAandB/src/android/permission/cts/appthatrequestpermission/RequestPermission.java b/tests/cts/permission/AppThatRequestPermissionAandB/src/android/permission/cts/appthatrequestpermission/RequestPermission.java
new file mode 100644
index 000000000..26671beb7
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandB/src/android/permission/cts/appthatrequestpermission/RequestPermission.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrequestpermission;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class RequestPermission extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestPermissions(new String[] {"android.permission.cts.appthatrequestpermission.A",
+ "android.permission.cts.B"}, 0);
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestPermissionAandC/Android.bp b/tests/cts/permission/AppThatRequestPermissionAandC/Android.bp
new file mode 100644
index 000000000..b94965334
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandC/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsAppThatRequestsPermissionAandC",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ srcs: ["src/**/*.java"],
+}
diff --git a/tests/cts/permission/AppThatRequestPermissionAandC/AndroidManifest.xml b/tests/cts/permission/AppThatRequestPermissionAandC/AndroidManifest.xml
new file mode 100644
index 000000000..9b998311b
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandC/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.appthatrequestpermission">
+
+ <permission android:name="android.permission.cts.appthatrequestpermission.A"
+ android:protectionLevel="dangerous"
+ android:label="@string/perm_a"
+ android:permissionGroup="android.permission.cts.groupC"
+ android:description="@string/perm_a" />
+
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.A" />
+ <uses-permission android:name="android.permission.cts.C" />
+
+ <application>
+ <activity android:name=".RequestPermission" android:exported="true"
+ android:visibleToInstantApps="true" />
+ </application>
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestPermissionAandC/res/values/strings.xml b/tests/cts/permission/AppThatRequestPermissionAandC/res/values/strings.xml
new file mode 100644
index 000000000..563009789
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandC/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="perm_a">Permission A</string>
+</resources>
diff --git a/tests/cts/permission/AppThatRequestPermissionAandC/src/android/permission/cts/appthatrequestpermission/RequestPermission.java b/tests/cts/permission/AppThatRequestPermissionAandC/src/android/permission/cts/appthatrequestpermission/RequestPermission.java
new file mode 100644
index 000000000..ad72c4db2
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestPermissionAandC/src/android/permission/cts/appthatrequestpermission/RequestPermission.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrequestpermission;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class RequestPermission extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestPermissions(new String[] {"android.permission.cts.appthatrequestpermission.A",
+ "android.permission.cts.C"}, 0);
+ }
+}
diff --git a/tests/cts/permission/AppThatRequestStoragePermission22/Android.bp b/tests/cts/permission/AppThatRequestStoragePermission22/Android.bp
new file mode 100644
index 000000000..d0b5e68a8
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestStoragePermission22/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatRequestsStoragePermission22",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml b/tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml
new file mode 100644
index 000000000..5c92a7ea6
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestStoragePermission22/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestStoragePermission28/Android.bp b/tests/cts/permission/AppThatRequestStoragePermission28/Android.bp
new file mode 100644
index 000000000..50ae9209b
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestStoragePermission28/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppThatRequestsStoragePermission28",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestStoragePermission28/AndroidManifest.xml b/tests/cts/permission/AppThatRequestStoragePermission28/AndroidManifest.xml
new file mode 100644
index 000000000..a847f39bd
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestStoragePermission28/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestStoragePermission29/Android.bp b/tests/cts/permission/AppThatRequestStoragePermission29/Android.bp
new file mode 100644
index 000000000..4663be9dc
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestStoragePermission29/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppThatRequestsStoragePermission29",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppThatRequestStoragePermission29/AndroidManifest.xml b/tests/cts/permission/AppThatRequestStoragePermission29/AndroidManifest.xml
new file mode 100644
index 000000000..c783085a0
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestStoragePermission29/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permission.cts.appthatrequestpermission"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppThatRequestSystemAlertWindow22/Android.bp b/tests/cts/permission/AppThatRequestSystemAlertWindow22/Android.bp
new file mode 100644
index 000000000..bafa460f1
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestSystemAlertWindow22/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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_helper_app {
+ name: "CtsAppThatRequestsSystemAlertWindow22",
+ target_sdk_version: "22",
+ certificate: ":cts-testkey2",
+ min_sdk_version: "22",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml b/tests/cts/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml
new file mode 100644
index 000000000..bd13612d2
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestSystemAlertWindow22/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.permission.cts.usesystemalertwindowpermission">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+</manifest>
diff --git a/tests/cts/permission/AppThatRequestSystemAlertWindow23/Android.bp b/tests/cts/permission/AppThatRequestSystemAlertWindow23/Android.bp
new file mode 100644
index 000000000..6c35f1881
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestSystemAlertWindow23/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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_helper_app {
+ name: "CtsAppThatRequestsSystemAlertWindow23",
+ target_sdk_version: "23",
+ certificate: ":cts-testkey2",
+ min_sdk_version: "23",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml b/tests/cts/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml
new file mode 100644
index 000000000..bd13612d2
--- /dev/null
+++ b/tests/cts/permission/AppThatRequestSystemAlertWindow23/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.permission.cts.usesystemalertwindowpermission">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+</manifest>
diff --git a/tests/cts/permission/AppThatRunsRationaleTests/Android.bp b/tests/cts/permission/AppThatRunsRationaleTests/Android.bp
new file mode 100644
index 000000000..30019fba5
--- /dev/null
+++ b/tests/cts/permission/AppThatRunsRationaleTests/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppThatRunsRationaleTests",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "test_current",
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+
+ srcs: ["src/**/*.java"],
+}
diff --git a/tests/cts/permission/AppThatRunsRationaleTests/AndroidManifest.xml b/tests/cts/permission/AppThatRunsRationaleTests/AndroidManifest.xml
new file mode 100644
index 000000000..4b7214fd6
--- /dev/null
+++ b/tests/cts/permission/AppThatRunsRationaleTests/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permission.cts.appthatrunsrationaletests">
+
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+
+ <application android:label="CtsRationaleTests">
+ <activity android:name=".TestActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java b/tests/cts/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java
new file mode 100644
index 000000000..7544890ff
--- /dev/null
+++ b/tests/cts/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrunsrationaletests;
+
+import android.Manifest;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+public class TestActivity extends Activity {
+ private static final String CALLBACK_KEY = "testactivitycallback";
+ private static final String RESULT_KEY = "testactivityresult";
+ private static final String PERMISSION_NAME = Manifest.permission.READ_CONTACTS;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ RemoteCallback cb = (RemoteCallback) getIntent().getExtras().get(CALLBACK_KEY);
+
+ boolean result = shouldShowRequestPermissionRationale(PERMISSION_NAME);
+ Bundle res = new Bundle();
+ res.putBoolean(RESULT_KEY, result);
+
+ finish();
+ cb.sendResult(res);
+ }
+}
diff --git a/tests/cts/permission/AppToTestRevokeSelfPermission/Android.bp b/tests/cts/permission/AppToTestRevokeSelfPermission/Android.bp
new file mode 100644
index 000000000..8898ce2d9
--- /dev/null
+++ b/tests/cts/permission/AppToTestRevokeSelfPermission/Android.bp
@@ -0,0 +1,36 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsAppToTestRevokeSelfPermission",
+ defaults: [
+ "cts_defaults",
+ "mts-target-sdk-version-current",
+ ],
+ min_sdk_version: "30",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "mts",
+ "general-tests",
+ ],
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/AppToTestRevokeSelfPermission/AndroidManifest.xml b/tests/cts/permission/AppToTestRevokeSelfPermission/AndroidManifest.xml
new file mode 100644
index 000000000..dbe58bfd5
--- /dev/null
+++ b/tests/cts/permission/AppToTestRevokeSelfPermission/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 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.permission.cts.apptotestrevokeselfpermission">
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
+
+ <application>
+ <activity android:name=".RevokePermission" android:exported="true"
+ android:visibleToInstantApps="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java b/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java
new file mode 100644
index 000000000..b9a0ed7bb
--- /dev/null
+++ b/tests/cts/permission/AppToTestRevokeSelfPermission/src/android/permission/cts/apptotestselfrevokepermission/RevokePermission.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.apptotestrevokeselfpermission;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.Arrays;
+
+public class RevokePermission extends Activity {
+ private static final String TAG = "RevokePermission";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)");
+ return;
+ }
+
+ Intent intent = getIntent();
+ String[] permissions = intent.getStringArrayExtra("permissions");
+ if (permissions == null) {
+ return;
+ }
+ if (permissions.length == 1) {
+ getApplicationContext().revokeSelfPermissionOnKill(permissions[0]);
+ } else {
+ getApplicationContext().revokeSelfPermissionsOnKill(Arrays.asList(permissions));
+ }
+ }
+}
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
new file mode 100644
index 000000000..8214c425d
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppWithSharedUidThatRequestsLocationPermission28",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml
new file mode 100644
index 000000000..ec69d1541
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission28/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.appthatrequestpermission"
+ android:versionCode="2"
+ android:sharedUserId="android.permission.cts.appthatrequestpermission">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
new file mode 100644
index 000000000..3df5c9a7d
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppWithSharedUidThatRequestsLocationPermission29",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml
new file mode 100644
index 000000000..234192259
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestLocationPermission29/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.appthatrequestpermission"
+ android:versionCode="1"
+ android:sharedUserId="android.permission.cts.appthatrequestpermission">
+
+ <!-- STOPSHIP: Set to apk level that shipped the location tristate -->
+ <!-- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> -->
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp b/tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp
new file mode 100644
index 000000000..7dd3ef638
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppWithSharedUidThatRequestsNoPermissions",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/AndroidManifest.xml b/tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/AndroidManifest.xml
new file mode 100644
index 000000000..0b34036ec
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestsNoPermissions/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.appthatrequestnopermission"
+ android:sharedUserId="cts.permissions">
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestsPermissions/Android.bp b/tests/cts/permission/AppWithSharedUidThatRequestsPermissions/Android.bp
new file mode 100644
index 000000000..c58b3e81e
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestsPermissions/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAppWithSharedUidThatRequestsPermissions",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/AppWithSharedUidThatRequestsPermissions/AndroidManifest.xml b/tests/cts/permission/AppWithSharedUidThatRequestsPermissions/AndroidManifest.xml
new file mode 100644
index 000000000..ce02f17e1
--- /dev/null
+++ b/tests/cts/permission/AppWithSharedUidThatRequestsPermissions/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.appthatrequestpermission"
+ android:sharedUserId="cts.permissions">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+
+ <application />
+</manifest>
+
diff --git a/tests/cts/permission/OWNERS b/tests/cts/permission/OWNERS
new file mode 100644
index 000000000..6b284590c
--- /dev/null
+++ b/tests/cts/permission/OWNERS
@@ -0,0 +1,12 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
+
+per-file PowerManagerServicePermissionTest.java = file: platform/frameworks/base:/services/core/java/com/android/server/power/OWNERS
+per-file RequestLocation.java = tgunn@google.com
+
+per-file NoAudioPermissionTest.java = elaurent@google.com
+per-file MainlineNetworkStackPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS
+per-file Camera2PermissionTest.java = file: platform/frameworks/av:/camera/OWNERS
+per-file NoRollbackPermissionTest.java = mpgroover@google.com
+per-file EthernetManagerPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS \ No newline at end of file
diff --git a/tests/cts/permission/README b/tests/cts/permission/README
new file mode 100644
index 000000000..1ffc81f96
--- /dev/null
+++ b/tests/cts/permission/README
@@ -0,0 +1,16 @@
+Copyright (C) 2008 The Android Open Source Project
+
+Licensed under the Apache Licence, 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.
+
+In the permissions test cases, we just test the negative cases. These tests are to test the behavior of accessing the APIs without the required permission.
+
diff --git a/tests/cts/permission/StorageEscalationApp28/Android.bp b/tests/cts/permission/StorageEscalationApp28/Android.bp
new file mode 100644
index 000000000..3953e5e27
--- /dev/null
+++ b/tests/cts/permission/StorageEscalationApp28/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2016 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_helper_app {
+ name: "CtsStorageEscalationApp28",
+ certificate: ":cts-testkey2",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/StorageEscalationApp28/AndroidManifest.xml b/tests/cts/permission/StorageEscalationApp28/AndroidManifest.xml
new file mode 100644
index 000000000..7b468bbf1
--- /dev/null
+++ b/tests/cts/permission/StorageEscalationApp28/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2016 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.permission.cts.storageescalation">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+
+ <application android:hasCode="false" />
+</manifest>
diff --git a/tests/cts/permission/StorageEscalationApp29Full/Android.bp b/tests/cts/permission/StorageEscalationApp29Full/Android.bp
new file mode 100644
index 000000000..50ea8c60d
--- /dev/null
+++ b/tests/cts/permission/StorageEscalationApp29Full/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2016 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_helper_app {
+ name: "CtsStorageEscalationApp29Full",
+ certificate: ":cts-testkey2",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/StorageEscalationApp29Full/AndroidManifest.xml b/tests/cts/permission/StorageEscalationApp29Full/AndroidManifest.xml
new file mode 100644
index 000000000..0ed8e0024
--- /dev/null
+++ b/tests/cts/permission/StorageEscalationApp29Full/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2016 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.permission.cts.storageescalation">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+
+ <application android:hasCode="false" android:requestLegacyExternalStorage="true"/>
+</manifest>
diff --git a/tests/cts/permission/StorageEscalationApp29Scoped/Android.bp b/tests/cts/permission/StorageEscalationApp29Scoped/Android.bp
new file mode 100644
index 000000000..716e31e09
--- /dev/null
+++ b/tests/cts/permission/StorageEscalationApp29Scoped/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2016 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_helper_app {
+ name: "CtsStorageEscalationApp29Scoped",
+ certificate: ":cts-testkey2",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/StorageEscalationApp29Scoped/AndroidManifest.xml b/tests/cts/permission/StorageEscalationApp29Scoped/AndroidManifest.xml
new file mode 100644
index 000000000..0ce57c1b5
--- /dev/null
+++ b/tests/cts/permission/StorageEscalationApp29Scoped/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2016 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.permission.cts.storageescalation">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+
+ <application android:hasCode="false" android:requestLegacyExternalStorage="false"/>
+</manifest>
diff --git a/tests/cts/permission/jni/Android.bp b/tests/cts/permission/jni/Android.bp
new file mode 100644
index 000000000..59f93a098
--- /dev/null
+++ b/tests/cts/permission/jni/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2010 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"],
+}
+
+cc_test_library {
+ name: "libctspermission_jni",
+ sdk_version: "current",
+ srcs: [
+ "CtsPermissionsJniOnLoad.cpp",
+ "android_permission_cts_FileUtils.cpp",
+ ],
+ shared_libs: [
+ "libnativehelper_compat_libc++",
+ "liblog",
+ ],
+ stl: "c++_static",
+ cflags: [
+ "-Wno-unused-parameter",
+ ],
+ gtest: false,
+}
+
+cc_test_library {
+ name: "libpermissionmanager_native_test",
+ sdk_version: "current",
+ compile_multilib: "both",
+ srcs: [
+ "PermissionManagerNativeJniTest.cpp"
+ ],
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ ],
+ static_libs: [
+ "libbase_ndk",
+ ],
+ whole_static_libs: [
+ "libnativetesthelper_jni"
+ ],
+ gtest: false,
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+}
diff --git a/tests/cts/permission/jni/CtsPermissionsJniOnLoad.cpp b/tests/cts/permission/jni/CtsPermissionsJniOnLoad.cpp
new file mode 100644
index 000000000..fab33bdc7
--- /dev/null
+++ b/tests/cts/permission/jni/CtsPermissionsJniOnLoad.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_permission_cts_FileUtils(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ JNIEnv *env = NULL;
+
+ if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ if (register_android_permission_cts_FileUtils(env)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_4;
+}
diff --git a/tests/cts/permission/jni/PermissionManagerNativeJniTest.cpp b/tests/cts/permission/jni/PermissionManagerNativeJniTest.cpp
new file mode 100644
index 000000000..392007074
--- /dev/null
+++ b/tests/cts/permission/jni/PermissionManagerNativeJniTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "PermissionManagerNativeJniTest"
+
+#include <android/permission_manager.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+class PermissionManagerNativeJniTest : public ::testing::Test {
+public:
+ void SetUp() override { }
+ void TearDown() override { }
+};
+
+//-------------------------------------------------------------------------------------------------
+TEST_F(PermissionManagerNativeJniTest, testCheckPermission) {
+ pid_t selfPid = ::getpid();
+ uid_t selfUid = ::getuid();
+
+ LOG(INFO) << "testCheckPermission: uid " << selfUid << ", pid" << selfPid;
+
+ int32_t result;
+ // Check some permission(s) we should have.
+ EXPECT_EQ(APermissionManager_checkPermission("android.permission.ACCESS_FINE_LOCATION",
+ selfPid, selfUid, &result),
+ PERMISSION_MANAGER_STATUS_OK);
+ EXPECT_EQ(result, PERMISSION_MANAGER_PERMISSION_GRANTED);
+
+ // Check some permission(s) we should not have.
+ EXPECT_EQ(APermissionManager_checkPermission("android.permission.MANAGE_USERS",
+ selfPid, selfUid, &result),
+ PERMISSION_MANAGER_STATUS_OK);
+ EXPECT_EQ(result, PERMISSION_MANAGER_PERMISSION_DENIED);
+}
+
diff --git a/tests/cts/permission/jni/android_permission_cts_FileUtils.cpp b/tests/cts/permission/jni/android_permission_cts_FileUtils.cpp
new file mode 100644
index 000000000..68c3c76d3
--- /dev/null
+++ b/tests/cts/permission/jni/android_permission_cts_FileUtils.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include <android/log.h>
+#include <jni.h>
+#include <stdio.h>
+#include <linux/xattr.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <sys/capability.h>
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+static jfieldID gFileStatusDevFieldID;
+static jfieldID gFileStatusInoFieldID;
+static jfieldID gFileStatusModeFieldID;
+static jfieldID gFileStatusNlinkFieldID;
+static jfieldID gFileStatusUidFieldID;
+static jfieldID gFileStatusGidFieldID;
+static jfieldID gFileStatusSizeFieldID;
+static jfieldID gFileStatusBlksizeFieldID;
+static jfieldID gFileStatusBlocksFieldID;
+static jfieldID gFileStatusAtimeFieldID;
+static jfieldID gFileStatusMtimeFieldID;
+static jfieldID gFileStatusCtimeFieldID;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/permission/src/android/permission/cts/FileUtils.java
+ *
+ * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
+ */
+
+jboolean android_permission_cts_FileUtils_getFileStatus(JNIEnv* env,
+ jobject /* thiz */, jstring path, jobject fileStatus, jboolean statLinks)
+{
+ ScopedUtfChars cPath(env, path);
+ jboolean ret = false;
+ struct stat s;
+
+ int res = statLinks == true ? lstat(cPath.c_str(), &s)
+ : stat(cPath.c_str(), &s);
+
+ if (res == 0) {
+ ret = true;
+ if (fileStatus != NULL) {
+ env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
+ env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
+ env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
+ env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
+ env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
+ env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
+ env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
+ env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
+ env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
+ env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
+ env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
+ env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
+ }
+ }
+
+ return ret;
+}
+
+jstring android_permission_cts_FileUtils_getUserName(JNIEnv* env,
+ jobject /* thiz */, jint uid)
+{
+ struct passwd *pwd = getpwuid(uid);
+ return env->NewStringUTF(pwd->pw_name);
+}
+
+jstring android_permission_cts_FileUtils_getGroupName(JNIEnv* env,
+ jobject /* thiz */, jint gid)
+{
+ struct group *grp = getgrgid(gid);
+ return env->NewStringUTF(grp->gr_name);
+}
+
+static jboolean isPermittedCapBitSet(JNIEnv* env, jstring path, size_t capId)
+{
+ struct vfs_cap_data capData;
+ memset(&capData, 0, sizeof(capData));
+
+ ScopedUtfChars cPath(env, path);
+ ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &capData,
+ sizeof(capData));
+ if (result <= 0)
+ {
+ __android_log_print(ANDROID_LOG_DEBUG, NULL,
+ "isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
+ "return %zd (error: %s (%d))\n",
+ cPath.c_str(), result, strerror(errno), errno);
+ return false;
+ }
+
+ return (capData.data[CAP_TO_INDEX(capId)].permitted &
+ CAP_TO_MASK(capId)) != 0;
+}
+
+jboolean android_permission_cts_FileUtils_hasSetUidCapability(JNIEnv* env,
+ jobject /* clazz */, jstring path)
+{
+ return isPermittedCapBitSet(env, path, CAP_SETUID);
+}
+
+jboolean android_permission_cts_FileUtils_hasSetGidCapability(JNIEnv* env,
+ jobject /* clazz */, jstring path)
+{
+ return isPermittedCapBitSet(env, path, CAP_SETGID);
+}
+
+static bool throwNamedException(JNIEnv* env, const char* className,
+ const char* message)
+{
+ ScopedLocalRef<jclass> eClazz(env, env->FindClass(className));
+ if (eClazz.get() == NULL)
+ {
+ __android_log_print(ANDROID_LOG_ERROR, NULL,
+ "throwNamedException(): failed to find class %s, cannot throw",
+ className);
+ return false;
+ }
+
+ env->ThrowNew(eClazz.get(), message);
+ return true;
+}
+
+// fill vfs_cap_data's permitted caps given a Java int[] of cap ids
+static bool fillPermittedCaps(vfs_cap_data* capData, JNIEnv* env, jintArray capIds)
+{
+ ScopedIntArrayRO cCapIds(env, capIds);
+ const size_t capCount = cCapIds.size();
+
+ for (size_t i = 0; i < capCount; ++i)
+ {
+ const jint capId = cCapIds[i];
+ if (!cap_valid(capId))
+ {
+ char message[64];
+ snprintf(message, sizeof(message),
+ "capability id %d out of valid range", capId);
+ throwNamedException(env, "java/lang/IllegalArgumentException",
+ message);
+
+ return false;
+ }
+ capData->data[CAP_TO_INDEX(capId)].permitted |= CAP_TO_MASK(capId);
+ }
+ return true;
+}
+
+jboolean android_permission_cts_FileUtils_CapabilitySet_fileHasOnly(JNIEnv* env,
+ jobject /* clazz */, jstring path, jintArray capIds)
+{
+ struct vfs_cap_data expectedCapData;
+ memset(&expectedCapData, 0, sizeof(expectedCapData));
+
+ expectedCapData.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
+ if (!fillPermittedCaps(&expectedCapData, env, capIds))
+ {
+ // exception thrown
+ return false;
+ }
+
+ struct vfs_cap_data actualCapData;
+ memset(&actualCapData, 0, sizeof(actualCapData));
+
+ ScopedUtfChars cPath(env, path);
+ ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &actualCapData,
+ sizeof(actualCapData));
+ if (result <= 0)
+ {
+ __android_log_print(ANDROID_LOG_DEBUG, NULL,
+ "fileHasOnly(): getxattr(\"%s\") call failed: "
+ "return %zd (error: %s (%d))\n",
+ cPath.c_str(), result, strerror(errno), errno);
+ return false;
+ }
+
+ return (memcmp(&expectedCapData, &actualCapData,
+ sizeof(struct vfs_cap_data)) == 0);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "getFileStatus", "(Ljava/lang/String;Landroid/permission/cts/FileUtils$FileStatus;Z)Z",
+ (void *) android_permission_cts_FileUtils_getFileStatus },
+ { "getUserName", "(I)Ljava/lang/String;",
+ (void *) android_permission_cts_FileUtils_getUserName },
+ { "getGroupName", "(I)Ljava/lang/String;",
+ (void *) android_permission_cts_FileUtils_getGroupName },
+ { "hasSetUidCapability", "(Ljava/lang/String;)Z",
+ (void *) android_permission_cts_FileUtils_hasSetUidCapability },
+ { "hasSetGidCapability", "(Ljava/lang/String;)Z",
+ (void *) android_permission_cts_FileUtils_hasSetGidCapability },
+};
+
+static JNINativeMethod gCapabilitySetMethods[] = {
+ { "fileHasOnly", "(Ljava/lang/String;[I)Z",
+ (void *) android_permission_cts_FileUtils_CapabilitySet_fileHasOnly },
+};
+
+int register_android_permission_cts_FileUtils(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/permission/cts/FileUtils");
+
+ jclass fileStatusClass = env->FindClass("android/permission/cts/FileUtils$FileStatus");
+ gFileStatusDevFieldID = env->GetFieldID(fileStatusClass, "dev", "I");
+ gFileStatusInoFieldID = env->GetFieldID(fileStatusClass, "ino", "I");
+ gFileStatusModeFieldID = env->GetFieldID(fileStatusClass, "mode", "I");
+ gFileStatusNlinkFieldID = env->GetFieldID(fileStatusClass, "nlink", "I");
+ gFileStatusUidFieldID = env->GetFieldID(fileStatusClass, "uid", "I");
+ gFileStatusGidFieldID = env->GetFieldID(fileStatusClass, "gid", "I");
+ gFileStatusSizeFieldID = env->GetFieldID(fileStatusClass, "size", "J");
+ gFileStatusBlksizeFieldID = env->GetFieldID(fileStatusClass, "blksize", "I");
+ gFileStatusBlocksFieldID = env->GetFieldID(fileStatusClass, "blocks", "J");
+ gFileStatusAtimeFieldID = env->GetFieldID(fileStatusClass, "atime", "J");
+ gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
+ gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
+
+ jint result = env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+ if (result)
+ {
+ return result;
+ }
+
+ // register FileUtils.CapabilitySet native methods
+ jclass capClazz = env->FindClass("android/permission/cts/FileUtils$CapabilitySet");
+
+ return env->RegisterNatives(capClazz, gCapabilitySetMethods,
+ sizeof(gCapabilitySetMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/cts/permission/nativeTests/Android.bp b/tests/cts/permission/nativeTests/Android.bp
new file mode 100644
index 000000000..40f8b6e0d
--- /dev/null
+++ b/tests/cts/permission/nativeTests/Android.bp
@@ -0,0 +1,56 @@
+// 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CtsPermissionManagerNativeTestCases",
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["src/PermissionManagerNativeTest.cpp"],
+
+ shared_libs: [
+ "liblog",
+ "libandroid",
+ ],
+
+ static_libs: [
+ "libgtest_ndk_c++",
+ "libbase_ndk",
+ ],
+ stl: "libc++_static",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permission/nativeTests/AndroidTest.xml b/tests/cts/permission/nativeTests/AndroidTest.xml
new file mode 100644
index 000000000..f477231ef
--- /dev/null
+++ b/tests/cts/permission/nativeTests/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+<configuration description="Config for CTS PermissionManager native test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="false" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsPermissionManagerNativeTestCases->/data/local/tmp/CtsPermissionManagerNativeTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsPermissionManagerNativeTestCases" />
+ <option name="runtime-hint" value="15s" />
+ </test>
+
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm and device is x86 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
+</configuration>
diff --git a/tests/cts/permission/nativeTests/src/PermissionManagerNativeTest.cpp b/tests/cts/permission/nativeTests/src/PermissionManagerNativeTest.cpp
new file mode 100644
index 000000000..1b0dc06ea
--- /dev/null
+++ b/tests/cts/permission/nativeTests/src/PermissionManagerNativeTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "PermissionManagerNativeTest"
+
+#include <android/permission_manager.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+//-----------------------------------------------------------------
+class PermissionManagerNativeTest : public ::testing::Test {
+
+protected:
+ PermissionManagerNativeTest() { }
+
+ virtual ~PermissionManagerNativeTest() { }
+
+ /* Test setup*/
+ virtual void SetUp() { }
+
+ /* Test tear down */
+ virtual void TearDown() { }
+};
+
+//-------------------------------------------------------------------------------------------------
+TEST_F(PermissionManagerNativeTest, testCheckPermission) {
+ pid_t selfPid = ::getpid();
+ uid_t selfUid = ::getuid();
+
+ LOG(INFO) << "testCheckPermission: uid " << selfUid << ", pid" << selfPid;
+
+ // Test is set up to force unroot by RootTargetPreparer, so we should be running as SHELL.
+ // Check some permissions SHELL should definitely have or not have.
+ int32_t result;
+ EXPECT_EQ(APermissionManager_checkPermission("android.permission.DUMP",
+ selfPid, selfUid, &result),
+ PERMISSION_MANAGER_STATUS_OK);
+ EXPECT_EQ(result, PERMISSION_MANAGER_PERMISSION_GRANTED);
+
+ EXPECT_EQ(APermissionManager_checkPermission("android.permission.MANAGE_USERS",
+ selfPid, selfUid, &result),
+ PERMISSION_MANAGER_STATUS_OK);
+ EXPECT_EQ(result, PERMISSION_MANAGER_PERMISSION_DENIED);
+
+ EXPECT_EQ(APermissionManager_checkPermission("android.permission.NETWORK_STACK",
+ selfPid, selfUid, &result),
+ PERMISSION_MANAGER_STATUS_OK);
+ EXPECT_EQ(result, PERMISSION_MANAGER_PERMISSION_DENIED);
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/Android.bp b/tests/cts/permission/permissionTestUtilLib/Android.bp
new file mode 100644
index 000000000..2f7004d5f
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 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"],
+}
+
+java_library {
+ name: "permission-test-util-lib",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ visibility: [
+ "//packages/modules/Permission:__subpackages__",
+ "//cts:__subpackages__",
+ ],
+ static_libs: [
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "androidx.test.ext.junit-nodeps",
+ ],
+
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerHelperRule.kt b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerHelperRule.kt
new file mode 100644
index 000000000..acdf55ef1
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerHelperRule.kt
@@ -0,0 +1,56 @@
+/*
+ * 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 android.permission.cts
+
+import android.content.ComponentName
+import android.content.Context
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/** Rule that enables and disables the CTS NotificationListenerService */
+class CtsNotificationListenerHelperRule(context: Context) : TestRule {
+
+ private val notificationListenerComponentName =
+ ComponentName(context, CtsNotificationListenerService::class.java)
+
+ override fun apply(base: Statement, description: Description): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ try {
+ // Allow NLS used to verify notifications sent
+ SystemUtil.runShellCommand(
+ ALLOW_NLS_COMMAND + notificationListenerComponentName.flattenToString()
+ )
+
+ base.evaluate()
+ } finally {
+ // Disallow NLS used to verify notifications sent
+ SystemUtil.runShellCommand(
+ DISALLOW_NLS_COMMAND + notificationListenerComponentName.flattenToString()
+ )
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val ALLOW_NLS_COMMAND = "cmd notification allow_listener "
+ private const val DISALLOW_NLS_COMMAND = "cmd notification disallow_listener "
+ }
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerService.java b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerService.java
new file mode 100644
index 000000000..6ffdd6fcf
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerService.java
@@ -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 android.permission.cts;
+
+import android.service.notification.NotificationListenerService;
+import android.util.Log;
+
+/**
+ * Implementation of {@link NotificationListenerService} for CTS tests.
+ *
+ * <p>In order to use this service in a test suite, ensure this service is declared in the test
+ * suite's AndroidManifest.xml as follows:
+ *
+ * <pre>{@code
+ * <service android:name="android.permission.cts.CtsNotificationListenerService"
+ * android:exported="true"
+ * android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.notification.NotificationListenerService"/>
+ * </intent-filter>
+ * </service>
+ * }</pre>
+ */
+public class CtsNotificationListenerService extends NotificationListenerService {
+ private static final String LOG_TAG = CtsNotificationListenerService.class.getSimpleName();
+
+ private static final Object sLock = new Object();
+
+ private static CtsNotificationListenerService sService;
+
+ @Override
+ public void onListenerConnected() {
+ Log.i(LOG_TAG, "connected");
+ synchronized (sLock) {
+ sService = this;
+ sLock.notifyAll();
+ }
+ }
+
+ public static NotificationListenerService getInstance() throws Exception {
+ synchronized (sLock) {
+ if (sService == null) {
+ sLock.wait(5000);
+ }
+
+ return sService;
+ }
+ }
+
+ @Override
+ public void onListenerDisconnected() {
+ Log.i(LOG_TAG, "disconnected");
+
+ synchronized (sLock) {
+ sService = null;
+ }
+ }
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerServiceUtils.kt b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerServiceUtils.kt
new file mode 100644
index 000000000..15d091f72
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/CtsNotificationListenerServiceUtils.kt
@@ -0,0 +1,127 @@
+/*
+ * 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 android.permission.cts
+
+import android.permission.cts.TestUtils.ensure
+import android.permission.cts.TestUtils.eventually
+import android.service.notification.StatusBarNotification
+import org.junit.Assert
+
+object CtsNotificationListenerServiceUtils {
+
+ private const val NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS = 5000L
+ private const val NOTIFICATION_WAIT_MILLIS = 2000L
+
+ @JvmStatic
+ fun assertEmptyNotification(packageName: String, notificationId: Int) {
+ ensure(
+ {
+ Assert.assertNull(
+ "Expected no notification",
+ getNotification(packageName, notificationId)
+ )
+ },
+ NOTIFICATION_WAIT_MILLIS
+ )
+ }
+
+ @JvmStatic
+ fun assertNotificationExist(packageName: String, notificationId: Int) {
+ eventually(
+ {
+ Assert.assertNotNull(
+ "Expected notification, none found",
+ getNotification(packageName, notificationId)
+ )
+ },
+ NOTIFICATION_WAIT_MILLIS
+ )
+ }
+
+ @JvmStatic
+ fun cancelNotification(packageName: String, notificationId: Int) {
+ val notificationService = CtsNotificationListenerService.getInstance()
+ val notification = getNotification(packageName, notificationId)
+ if (notification != null) {
+ notificationService.cancelNotification(notification.key)
+ eventually(
+ { Assert.assertTrue(getNotification(packageName, notificationId) == null) },
+ NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS
+ )
+ }
+ }
+
+ @JvmStatic
+ fun cancelNotifications(packageName: String) {
+ val notificationService = CtsNotificationListenerService.getInstance()
+ val notifications = getNotifications(packageName)
+ if (notifications.isNotEmpty()) {
+ notifications.forEach { notification ->
+ notificationService.cancelNotification(notification.key)
+ }
+ eventually(
+ { Assert.assertTrue(getNotifications(packageName).isEmpty()) },
+ NOTIFICATION_CANCELLATION_TIMEOUT_MILLIS
+ )
+ }
+ }
+
+ @JvmStatic
+ fun getNotification(packageName: String, notificationId: Int): StatusBarNotification? {
+ return getNotifications(packageName).firstOrNull { it.id == notificationId }
+ }
+
+ @JvmStatic
+ fun getNotifications(packageName: String): List<StatusBarNotification> {
+ val notifications: MutableList<StatusBarNotification> = ArrayList()
+ val notificationService = CtsNotificationListenerService.getInstance()
+ for (notification in notificationService.activeNotifications) {
+ if (notification.packageName == packageName) {
+ notifications.add(notification)
+ }
+ }
+ return notifications
+ }
+
+ /**
+ * Get a notification listener notification that is currently visible.
+ *
+ * @param cancelNotification if `true` the notification is canceled inside this method
+ * @return The notification or `null` if there is none
+ */
+ @JvmStatic
+ @Throws(Throwable::class)
+ fun getNotificationForPackageAndId(
+ pkg: String,
+ id: Int,
+ cancelNotification: Boolean
+ ): StatusBarNotification? {
+ val notifications: List<StatusBarNotification> = getNotifications(pkg)
+ if (notifications.isEmpty()) {
+ return null
+ }
+ for (notification in notifications) {
+ if (notification.id == id) {
+ if (cancelNotification) {
+ cancelNotification(pkg, id)
+ }
+ return notification
+ }
+ }
+ return null
+ }
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/MtsIgnore.java b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/MtsIgnore.java
new file mode 100644
index 000000000..54c0c9a9c
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/MtsIgnore.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.permission.cts;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface MtsIgnore {
+ /**
+ * An optional bug number associated with the test. -1 Means that no bug number is associated.
+ *
+ * @return int
+ */
+ int bugId() default -1;
+
+ /**
+ * Details, such as the reason of why we're ignoring the test.
+ *
+ * @return String
+ */
+ String detail() default "";
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/PermissionUtils.java b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/PermissionUtils.java
new file mode 100644
index 000000000..6bbf8b52e
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/PermissionUtils.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
+import static android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS;
+import static android.Manifest.permission.MANAGE_APP_OPS_MODES;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OPSTR_GET_USAGE_STATS;
+import static android.app.AppOpsManager.permissionToOp;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.permission.cts.TestUtils.awaitJobUntilRequestedState;
+
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.waitForBroadcastDispatch;
+
+import android.app.AppOpsManager;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Common utils for permission tests
+ */
+public class PermissionUtils {
+ private static final int TESTED_FLAGS = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED
+ | FLAG_PERMISSION_REVOKE_ON_UPGRADE | FLAG_PERMISSION_REVIEW_REQUIRED
+ | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+
+ private static final String LOG_TAG = PermissionUtils.class.getSimpleName();
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private static final UiAutomation sUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ private PermissionUtils() {
+ // this class should never be instantiated
+ }
+
+ /**
+ * Get the state of an app-op.
+ *
+ * @param packageName The package the app-op belongs to
+ * @param permission The permission the app-op belongs to
+ *
+ * @return The mode the op is on
+ */
+ public static int getAppOp(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
+ return sContext.getSystemService(AppOpsManager.class).unsafeCheckOpRaw(
+ permissionToOp(permission),
+ sContext.getPackageManager().getPackageUid(packageName, 0), packageName);
+ }
+
+ /**
+ * Install an APK.
+ *
+ * @param apkFile The apk to install
+ */
+ public static void install(@NonNull String apkFile) {
+ final int sdkVersion = Build.VERSION.SDK_INT
+ + (Build.VERSION.RELEASE_OR_CODENAME.equals("REL") ? 0 : 1);
+ boolean forceQueryable = sdkVersion > Build.VERSION_CODES.Q;
+ runShellCommandOrThrow("pm install -r --force-sdk "
+ + (SdkLevel.isAtLeastU() ? "--bypass-low-target-sdk-block " : "")
+ + (forceQueryable ? "--force-queryable " : "")
+ + apkFile);
+ }
+
+ /**
+ * Uninstall a package.
+ *
+ * @param packageName Name of package to be uninstalled
+ */
+ public static void uninstallApp(@NonNull String packageName) {
+ runShellCommand("pm uninstall " + packageName);
+ }
+
+ /**
+ * Set a new state for an app-op (using the permission-name)
+ *
+ * @param packageName The package the app-op belongs to
+ * @param permission The permission the app-op belongs to
+ * @param mode The new mode
+ */
+ public static void setAppOp(@NonNull String packageName, @NonNull String permission, int mode) {
+ setAppOpByName(packageName, permissionToOp(permission), mode);
+ }
+
+ /**
+ * Set a new state for an app-op (using the app-op-name)
+ *
+ * @param packageName The package the app-op belongs to
+ * @param op The name of the op
+ * @param mode The new mode
+ */
+ public static void setAppOpByName(@NonNull String packageName, @NonNull String op, int mode) {
+ runWithShellPermissionIdentity(
+ () -> sContext.getSystemService(AppOpsManager.class).setUidMode(op,
+ sContext.getPackageManager().getPackageUid(packageName, 0), mode),
+ MANAGE_APP_OPS_MODES);
+ }
+
+ /**
+ * Checks a permission. Does <u>not</u> check the appOp.
+ *
+ * <p>Users should use {@link #isGranted} instead.
+ *
+ * @param packageName The package that might have the permission granted
+ * @param permission The permission that might be granted
+ *
+ * @return {@code true} iff the permission is granted
+ */
+ public static boolean isPermissionGranted(@NonNull String packageName,
+ @NonNull String permission) throws Exception {
+ return sContext.checkPermission(permission, Process.myPid(),
+ sContext.getPackageManager().getPackageUid(packageName, 0))
+ == PERMISSION_GRANTED;
+ }
+
+ /**
+ * Checks if a permission is granted for a package.
+ *
+ * <p>This correctly handles pre-M apps by checking the app-ops instead.
+ * <p>This also correctly handles the location background permission, but does not handle any
+ * other background permission
+ *
+ * @param packageName The package that might have the permission granted
+ * @param permission The permission that might be granted
+ *
+ * @return {@code true} iff the permission is granted
+ */
+ public static boolean isGranted(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
+ if (!isPermissionGranted(packageName, permission)) {
+ return false;
+ }
+
+ if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
+ // The app-op for background location is encoded into the mode of the foreground
+ // location
+ return getAppOp(packageName, ACCESS_COARSE_LOCATION) == MODE_ALLOWED;
+ } else {
+ int mode = getAppOp(packageName, permission);
+ return mode == MODE_ALLOWED || mode == MODE_FOREGROUND;
+ }
+ }
+
+ /**
+ * Grant a permission to an app.
+ *
+ * <p>This correctly handles pre-M apps by setting the app-ops.
+ * <p>This also correctly handles the location background permission, but does not handle any
+ * other background permission
+ *
+ * @param packageName The app that should have the permission granted
+ * @param permission The permission to grant
+ */
+ public static void grantPermission(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
+ sUiAutomation.grantRuntimePermission(packageName, permission);
+
+ if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
+ // The app-op for background location is encoded into the mode of the foreground
+ // location
+ if (isPermissionGranted(packageName, ACCESS_COARSE_LOCATION)) {
+ setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_ALLOWED);
+ } else {
+ setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
+ }
+ } else if (permission.equals(ACCESS_COARSE_LOCATION)) {
+ // The app-op for location depends on the state of the bg location
+ if (isPermissionGranted(packageName, ACCESS_BACKGROUND_LOCATION)) {
+ setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_ALLOWED);
+ } else {
+ setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
+ }
+ } else if (permission.equals(PACKAGE_USAGE_STATS)) {
+ setAppOpByName(packageName, OPSTR_GET_USAGE_STATS, MODE_ALLOWED);
+ } else if (permissionToOp(permission) != null) {
+ setAppOp(packageName, permission, MODE_ALLOWED);
+ }
+ }
+
+ /**
+ * Revoke a permission from an app.
+ *
+ * <p>This correctly handles pre-M apps by setting the app-ops.
+ * <p>This also correctly handles the location background permission, but does not handle any
+ * other background permission
+ *
+ * @param packageName The app that should have the permission revoked
+ * @param permission The permission to revoke
+ */
+ public static void revokePermission(@NonNull String packageName, @NonNull String permission)
+ throws Exception {
+ sUiAutomation.revokeRuntimePermission(packageName, permission);
+
+ if (permission.equals(ACCESS_BACKGROUND_LOCATION)) {
+ // The app-op for background location is encoded into the mode of the foreground
+ // location
+ if (isGranted(packageName, ACCESS_COARSE_LOCATION)) {
+ setAppOp(packageName, ACCESS_COARSE_LOCATION, MODE_FOREGROUND);
+ }
+ } else if (permission.equals(PACKAGE_USAGE_STATS)) {
+ setAppOpByName(packageName, OPSTR_GET_USAGE_STATS, MODE_IGNORED);
+ } else if (permissionToOp(permission) != null) {
+ setAppOp(packageName, permission, MODE_IGNORED);
+ }
+ }
+
+ /**
+ * Clear permission state (not app-op state) of package.
+ *
+ * @param packageName Package to clear
+ */
+ public static void clearAppState(@NonNull String packageName) {
+ runShellCommand("pm clear --user current " + packageName);
+ }
+
+ /**
+ * Get all the flags of a permission.
+ *
+ * @param packageName Package the permission belongs to
+ * @param permission Name of the permission
+ *
+ * @return Permission flags
+ */
+ public static int getAllPermissionFlags(@NonNull String packageName,
+ @NonNull String permission) {
+ try {
+ return callWithShellPermissionIdentity(
+ () -> sContext.getPackageManager().getPermissionFlags(permission, packageName,
+ UserHandle.getUserHandleForUid(Process.myUid())),
+ GRANT_RUNTIME_PERMISSIONS);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Get the flags of a permission.
+ *
+ * @param packageName Package the permission belongs to
+ * @param permission Name of the permission
+ *
+ * @return Permission flags
+ */
+ public static int getPermissionFlags(@NonNull String packageName, @NonNull String permission) {
+ return getAllPermissionFlags(packageName, permission) & TESTED_FLAGS;
+ }
+
+ /**
+ * Set the flags of a permission.
+ *
+ * @param packageName Package the permission belongs to
+ * @param permission Name of the permission
+ * @param mask Mask of permissions to set
+ * @param flags Permissions to set
+ */
+ public static void setPermissionFlags(@NonNull String packageName, @NonNull String permission,
+ int mask, int flags) {
+ runWithShellPermissionIdentity(
+ () -> sContext.getPackageManager().updatePermissionFlags(permission, packageName,
+ mask, flags, UserHandle.getUserHandleForUid(Process.myUid())),
+ GRANT_RUNTIME_PERMISSIONS, ADJUST_RUNTIME_PERMISSIONS_POLICY);
+ }
+
+ /**
+ * Get all permissions an app requests. This includes the split permissions.
+ *
+ * @param packageName The package that requests the permissions.
+ *
+ * @return The permissions requested by the app
+ */
+ public static @NonNull List<String> getPermissions(@NonNull String packageName)
+ throws Exception {
+ PackageInfo appInfo = sContext.getPackageManager().getPackageInfo(packageName,
+ GET_PERMISSIONS);
+
+ return Arrays.asList(appInfo.requestedPermissions);
+ }
+
+ /**
+ * Get all runtime permissions that an app requests. This includes the split permissions.
+ *
+ * @param packageName The package that requests the permissions.
+ *
+ * @return The runtime permissions requested by the app
+ */
+ public static @NonNull List<String> getRuntimePermissions(@NonNull String packageName)
+ throws Exception {
+ ArrayList<String> runtimePermissions = new ArrayList<>();
+
+ for (String perm : getPermissions(packageName)) {
+ PermissionInfo info = sContext.getPackageManager().getPermissionInfo(perm, 0);
+ if ((info.getProtection() & PROTECTION_DANGEROUS) != 0) {
+ runtimePermissions.add(perm);
+ }
+ }
+
+ return runtimePermissions;
+ }
+
+ /**
+ * Reset permission controller state & re-schedule the job.
+ */
+ public static void resetPermissionControllerJob(@NonNull UiAutomation automation,
+ @NonNull String packageName, int jobId, long timeout, @NonNull String intentAction,
+ @NonNull String onBootReceiver) throws Exception {
+ clearAppState(packageName);
+ awaitJobUntilRequestedState(packageName, jobId, timeout, automation, "unknown");
+ scheduleJob(automation, packageName, jobId, timeout, intentAction, onBootReceiver);
+
+ runShellCommand("cmd jobscheduler reset-execution-quota -u "
+ + Process.myUserHandle().getIdentifier() + " " + packageName);
+ runShellCommand("cmd jobscheduler reset-schedule-quota");
+ }
+
+ /**
+ * schedules a job for the privacy signal in Permission Controller
+ */
+ public static void scheduleJob(@NonNull UiAutomation automation,
+ @NonNull String packageName, int jobId, long timeout, @NonNull String intentAction,
+ @NonNull String broadcastReceiver) throws Exception {
+ long startTime = System.currentTimeMillis();
+ String jobStatus = "";
+ simulateReboot(packageName, intentAction, broadcastReceiver);
+
+ while ((System.currentTimeMillis() - startTime) < timeout
+ && !jobStatus.contains("waiting")) {
+ String cmd =
+ "cmd jobscheduler get-job-state -u " + Process.myUserHandle().getIdentifier()
+ + " " + packageName + " " + jobId;
+ jobStatus = runShellCommand(automation, cmd).trim();
+ Log.v(LOG_TAG, "Job: " + jobId + ", job status " + jobStatus);
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // ignore interrupt
+ }
+ }
+ if (!jobStatus.contains("waiting")) {
+ throw new IllegalStateException("The job didn't get scheduled in time.");
+ }
+ }
+
+ private static void simulateReboot(@NonNull String packageName, @NonNull String intentAction,
+ @NonNull String broadcastReceiver) {
+ Intent jobSetupReceiverIntent = new Intent(intentAction);
+ jobSetupReceiverIntent.setPackage(packageName);
+ jobSetupReceiverIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+ // Query for the setup broadcast receiver
+ List<ResolveInfo> resolveInfos =
+ sContext.getPackageManager().queryBroadcastReceivers(jobSetupReceiverIntent, 0);
+
+ if (resolveInfos.size() > 0) {
+ sContext.sendBroadcast(jobSetupReceiverIntent);
+ } else {
+ Intent intent = new Intent();
+ intent.setClassName(packageName, broadcastReceiver);
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setPackage(packageName);
+ sContext.sendBroadcast(intent);
+ }
+ waitForBroadcastDispatch(intentAction);
+ }
+}
diff --git a/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/TestUtils.java b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/TestUtils.java
new file mode 100644
index 000000000..48ccbe79f
--- /dev/null
+++ b/tests/cts/permission/permissionTestUtilLib/src/android/permission/cts/TestUtils.java
@@ -0,0 +1,205 @@
+/*
+ * 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 android.permission.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import android.app.UiAutomation;
+import android.os.Process;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assert;
+
+/** Common test utilities */
+public class TestUtils {
+ private static final String LOG_TAG = TestUtils.class.getSimpleName();
+
+ /**
+ * A {@link java.util.concurrent.Callable} that can throw a {@link Throwable}
+ */
+ public interface ThrowingCallable<T> {
+ T call() throws Throwable;
+ }
+
+ /**
+ * A {@link Runnable} that can throw a {@link Throwable}
+ */
+ public interface ThrowingRunnable {
+ void run() throws Throwable;
+ }
+
+ /**
+ * Make sure that a {@link ThrowingRunnable} finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingRunnable} to run.
+ * @param timeout the maximum time to wait
+ */
+ public static void ensure(@NonNull ThrowingRunnable r, long timeout) throws Throwable {
+ ensure(() -> {
+ r.run();
+ return 0;
+ }, timeout);
+ }
+
+ /**
+ * Make sure that a {@link ThrowingCallable} finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingCallable} to run.
+ * @param timeout the maximum time to wait
+ * @return the return value from the callable
+ * @throws NullPointerException If the return value never becomes non-null
+ */
+ public static <T> T ensure(@NonNull ThrowingCallable<T> r, long timeout) throws Throwable {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ T res = r.call();
+ if (res == null) {
+ throw new NullPointerException("No result");
+ }
+
+ if (System.currentTimeMillis() - start < timeout) {
+ Thread.sleep(500);
+ } else {
+ return res;
+ }
+ }
+ }
+
+ /**
+ * Make sure that a {@link ThrowingRunnable} eventually finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingRunnable} to run.
+ * @param timeout the maximum time to wait
+ */
+ public static void eventually(@NonNull ThrowingRunnable r, long timeout) throws Throwable {
+ eventually(() -> {
+ r.run();
+ return 0;
+ }, timeout);
+ }
+
+ /**
+ * Make sure that a {@link ThrowingCallable} eventually finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingCallable} to run.
+ * @param timeout the maximum time to wait
+ * @return the return value from the callable
+ * @throws NullPointerException If the return value never becomes non-null
+ */
+ public static <T> T eventually(@NonNull ThrowingCallable<T> r, long timeout) throws Throwable {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ T res = r.call();
+ if (res == null) {
+ throw new NullPointerException("No result");
+ }
+
+ return res;
+ } catch (Throwable e) {
+ if (System.currentTimeMillis() - start < timeout) {
+ Log.d(LOG_TAG, "Ignoring exception, occurred within valid wait time", e);
+
+ Thread.sleep(500);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Run the job and then wait for completion
+ */
+ public static void runJobAndWaitUntilCompleted(
+ String packageName,
+ int jobId, long timeout) {
+ runJobAndWaitUntilCompleted(packageName, jobId, timeout,
+ InstrumentationRegistry.getInstrumentation().getUiAutomation());
+ }
+
+ /**
+ * Run the job and then wait for completion
+ */
+ public static void runJobAndWaitUntilCompleted(
+ String packageName,
+ int jobId,
+ long timeout,
+ UiAutomation automation) {
+ String runJobCmd = "cmd jobscheduler run -u " + Process.myUserHandle().getIdentifier()
+ + " -f " + packageName + " " + jobId;
+ try {
+ String result = runShellCommand(automation, runJobCmd);
+ Log.v(LOG_TAG, "jobscheduler run job command output: " + result);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ // waiting state is expected after completion for the periodic jobs.
+ awaitJobUntilRequestedState(packageName, jobId, timeout, automation, "waiting");
+ }
+
+ public static void awaitJobUntilRequestedState(
+ String packageName,
+ int jobId,
+ long timeout,
+ UiAutomation automation,
+ String requestedState) {
+ String statusCmd = "cmd jobscheduler get-job-state -u "
+ + Process.myUserHandle().getIdentifier() + " " + packageName + " " + jobId;
+ try {
+ eventually(() -> {
+ String jobState = runShellCommand(automation, statusCmd).trim();
+ Assert.assertTrue("The job doesn't have requested state " + requestedState
+ + " yet, current state: " + jobState, jobState.startsWith(requestedState));
+ }, timeout);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void awaitJobUntilRequestedState(
+ String packageName,
+ int jobId,
+ long timeout,
+ UiAutomation automation,
+ String requestedState1,
+ String requestedState2) {
+ String statusCmd = "cmd jobscheduler get-job-state -u "
+ + Process.myUserHandle().getIdentifier() + " " + packageName + " " + jobId;
+ try {
+ eventually(() -> {
+ String jobState = runShellCommand(automation, statusCmd).trim();
+ boolean jobInEitherRequestedState = jobState.startsWith(requestedState1)
+ || jobState.startsWith(requestedState2);
+ Assert.assertTrue("The job doesn't have requested state "
+ + "(" + requestedState1 + " or " + requestedState2 + ")"
+ + " yet, current state: " + jobState, jobInEitherRequestedState);
+ }, timeout);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/cts/permission/res/drawable/robot.png b/tests/cts/permission/res/drawable/robot.png
new file mode 100644
index 000000000..8a9e6984b
--- /dev/null
+++ b/tests/cts/permission/res/drawable/robot.png
Binary files differ
diff --git a/tests/cts/permission/res/values/strings.xml b/tests/cts/permission/res/values/strings.xml
new file mode 100644
index 000000000..bebb179ec
--- /dev/null
+++ b/tests/cts/permission/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="perm_b">Permission B</string>
+ <string name="perm_c">Permission C</string>
+ <string name="perm_group_b">Permission group B</string>
+ <string name="perm_group_c">Permission group B</string>
+</resources>
diff --git a/tests/cts/permission/res/xml/test_accessibilityservice.xml b/tests/cts/permission/res/xml/test_accessibilityservice.xml
new file mode 100644
index 000000000..fa87e2e0f
--- /dev/null
+++ b/tests/cts/permission/res/xml/test_accessibilityservice.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:canRetrieveWindowContent="true"
+ android:accessibilityFlags="flagDefault"
+ android:notificationTimeout="0" />
diff --git a/tests/cts/permission/sdk28/Android.bp b/tests/cts/permission/sdk28/Android.bp
new file mode 100644
index 000000000..3043c9329
--- /dev/null
+++ b/tests/cts/permission/sdk28/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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: "CtsPermissionTestCasesSdk28",
+ defaults: ["cts_defaults"],
+ sdk_version: "28",
+ srcs: ["src/**/*.java"],
+ static_libs: ["ctstestrunner-axt"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+}
diff --git a/tests/cts/permission/sdk28/AndroidManifest.xml b/tests/cts/permission/sdk28/AndroidManifest.xml
new file mode 100644
index 000000000..1714052f7
--- /dev/null
+++ b/tests/cts/permission/sdk28/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.sdk28">
+
+ <uses-sdk android:minSdkVersion="3"
+ android:targetSdkVersion="28"
+ android:maxSdkVersion="28"/>
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.permission.cts.PermissionStubActivity"
+ android:label="PermissionStubActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!--
+ The CTS stubs package cannot be used as the target application here,
+ since that requires many permissions to be set. Instead, specify this
+ package itself as the target and include any stub activities needed.
+
+ This test package uses the default InstrumentationTestRunner, because
+ the InstrumentationCtsTestRunner is only available in the stubs
+ package. That runner cannot be added to this package either, since it
+ relies on hidden APIs.
+ -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permission.cts.sdk28"
+ android:label="CTS tests of legacy android permissions as of API 28">
+ </instrumentation>
+
+</manifest>
diff --git a/tests/cts/permission/sdk28/AndroidTest.xml b/tests/cts/permission/sdk28/AndroidTest.xml
new file mode 100644
index 000000000..391142964
--- /dev/null
+++ b/tests/cts/permission/sdk28/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Permission test cases for TargetSdk 28">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="not-shardable" value="true" />
+ <option name="config-descriptor:metadata" key="parameter" value="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="parameter" value="no_foldable_states" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionTestCasesSdk28.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permission.cts.sdk28" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/cts/permission/sdk28/OWNERS b/tests/cts/permission/sdk28/OWNERS
new file mode 100644
index 000000000..98dc09e9e
--- /dev/null
+++ b/tests/cts/permission/sdk28/OWNERS
@@ -0,0 +1 @@
+# Bug component: 137825
diff --git a/tests/cts/permission/sdk28/TEST_MAPPING b/tests/cts/permission/sdk28/TEST_MAPPING
new file mode 100644
index 000000000..b98bbaf43
--- /dev/null
+++ b/tests/cts/permission/sdk28/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPermissionTestCasesSdk28"
+ }
+ ]
+}
diff --git a/tests/cts/permission/sdk28/res/values/strings.xml b/tests/cts/permission/sdk28/res/values/strings.xml
new file mode 100644
index 000000000..9cc70f91a
--- /dev/null
+++ b/tests/cts/permission/sdk28/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sdk28">SDK Level 28</string>
+</resources>
diff --git a/tests/cts/permission/sdk28/src/android/permission/cts/sdk28/RequestLocation.java b/tests/cts/permission/sdk28/src/android/permission/cts/sdk28/RequestLocation.java
new file mode 100644
index 000000000..8ba39cdfe
--- /dev/null
+++ b/tests/cts/permission/sdk28/src/android/permission/cts/sdk28/RequestLocation.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.sdk28;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.telephony.NeighboringCellInfo;
+import android.telephony.TelephonyManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class RequestLocation {
+
+ private TelephonyManager mTelephonyManager;
+ private boolean mHasTelephony;
+
+ @Before
+ public void setUp() throws Exception {
+ mHasTelephony = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY);
+ mTelephonyManager = (TelephonyManager) getContext().getSystemService(
+ Context.TELEPHONY_SERVICE);
+ assertNotNull(mTelephonyManager);
+ }
+
+ /**
+ * Verify that a SecurityException is thrown when an app targeting SDK 28
+ * lacks the coarse location permission.
+ */
+ @Test
+ public void testGetNeighboringCellInfo() {
+ if (!mHasTelephony) return;
+ try {
+ List<NeighboringCellInfo> cellInfos = mTelephonyManager.getNeighboringCellInfo();
+ if (cellInfos != null && !cellInfos.isEmpty()) {
+ fail("Meaningful information returned from getNeighboringCellInfo!");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt b/tests/cts/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
new file mode 100644
index 000000000..227fbfb9a
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
@@ -0,0 +1,317 @@
+/*
+ * 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 android.permission.cts
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule
+import android.app.ActivityOptions
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.ComponentName
+import android.content.Context
+import android.os.Build
+import android.permission.cts.CtsNotificationListenerServiceUtils.assertEmptyNotification
+import android.permission.cts.CtsNotificationListenerServiceUtils.assertNotificationExist
+import android.permission.cts.CtsNotificationListenerServiceUtils.cancelNotification
+import android.permission.cts.CtsNotificationListenerServiceUtils.cancelNotifications
+import android.permission.cts.CtsNotificationListenerServiceUtils.getNotification
+import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueDoesNotExist
+import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueExist
+import android.permission.cts.SafetyCenterUtils.assertSafetyCenterStarted
+import android.permission.cts.SafetyCenterUtils.deleteDeviceConfigPrivacyProperty
+import android.permission.cts.SafetyCenterUtils.deviceSupportsSafetyCenter
+import android.permission.cts.SafetyCenterUtils.setDeviceConfigPrivacyProperty
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.rule.ScreenRecordRule
+import android.provider.DeviceConfig
+import android.safetycenter.SafetyCenterManager
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.modules.utils.build.SdkLevel
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@AppModeFull(
+ reason =
+ "Cannot set system settings as instant app. Also we never show an accessibility " +
+ "notification for instant apps."
+)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+@ScreenRecordRule.ScreenRecord
+@FlakyTest
+class AccessibilityPrivacySourceTest {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.targetContext
+ private val permissionControllerPackage = context.packageManager.permissionControllerPackageName
+ private val accessibilityTestService =
+ ComponentName(context, AccessibilityTestService::class.java).flattenToString()
+ private val safetyCenterIssueId = "accessibility_$accessibilityTestService"
+ private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)
+
+ @get:Rule val screenRecordRule = ScreenRecordRule(false, false)
+
+ @get:Rule
+ val mAccessibilityServiceRule =
+ InstrumentedAccessibilityServiceTestRule(AccessibilityTestService::class.java, false)
+
+ @get:Rule
+ val deviceConfigSafetyCenterEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_CENTER_ENABLED,
+ true.toString()
+ )
+
+ @get:Rule
+ val deviceConfigA11yListenerDisabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ ACCESSIBILITY_LISTENER_ENABLED,
+ false.toString()
+ )
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(deviceSupportsSafetyCenter(context))
+ InstrumentedAccessibilityService.disableAllServices()
+ runShellCommand("input keyevent KEYCODE_WAKEUP")
+ resetPermissionController()
+ cancelNotifications(permissionControllerPackage)
+ assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ runWithShellPermissionIdentity { safetyCenterManager?.clearAllSafetySourceDataForTests() }
+ assertSafetyCenterIssueDoesNotExist(
+ SC_ACCESSIBILITY_SOURCE_ID,
+ safetyCenterIssueId,
+ SC_ACCESSIBILITY_ISSUE_TYPE_ID
+ )
+ }
+
+ @After
+ fun cleanup() {
+ cancelNotifications(permissionControllerPackage)
+ runWithShellPermissionIdentity { safetyCenterManager?.clearAllSafetySourceDataForTests() }
+ }
+
+ @Test
+ fun testJobSendsNotification() {
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ }
+
+ @Test
+ fun testJobSendsNotificationOnEnable() {
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+
+ setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString())
+ cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ InstrumentedAccessibilityService.disableAllServices()
+ setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString())
+ setDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS, "0")
+
+ // enable service again and verify a notification
+ try {
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ } finally {
+ deleteDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS)
+ }
+ }
+
+ @Test
+ fun testJobSendsIssuesToSafetyCenter() {
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertSafetyCenterIssueExist(
+ SC_ACCESSIBILITY_SOURCE_ID,
+ safetyCenterIssueId,
+ SC_ACCESSIBILITY_ISSUE_TYPE_ID
+ )
+ }
+
+ @Test
+ fun testJobDoesNotSendNotificationInSecondRunForSameService() {
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+
+ cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+
+ runJobAndWaitUntilCompleted()
+ assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ }
+
+ @Test
+ fun testAccessibilityListenerSendsIssueToSafetyCenter() {
+ setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString())
+ try {
+ val automation = getAutomation()
+ mAccessibilityServiceRule.enableService()
+ TestUtils.eventually(
+ {
+ assertSafetyCenterIssueExist(
+ SC_ACCESSIBILITY_SOURCE_ID,
+ safetyCenterIssueId,
+ SC_ACCESSIBILITY_ISSUE_TYPE_ID,
+ automation
+ )
+ },
+ TIMEOUT_MILLIS
+ )
+ automation.destroy()
+ } finally {
+ setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString())
+ }
+ }
+
+ @Test
+ fun testJobWithDisabledServiceDoesNotSendNotification() {
+ runJobAndWaitUntilCompleted()
+ assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ }
+
+ @Test
+ fun testJobWithDisabledServiceDoesNotSendIssueToSafetyCenter() {
+ runJobAndWaitUntilCompleted()
+ assertSafetyCenterIssueDoesNotExist(
+ SC_ACCESSIBILITY_SOURCE_ID,
+ safetyCenterIssueId,
+ SC_ACCESSIBILITY_ISSUE_TYPE_ID
+ )
+ }
+
+ @Test
+ fun testJobWithSafetyCenterDisabledDoesNotSendNotification() {
+ setDeviceConfigPrivacyProperty(SAFETY_CENTER_ENABLED, false.toString())
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ }
+
+ @Test
+ fun testJobWithSafetyCenterDisabledDoesNotSendIssueToSafetyCenter() {
+ setDeviceConfigPrivacyProperty(SAFETY_CENTER_ENABLED, false.toString())
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertSafetyCenterIssueDoesNotExist(
+ SC_ACCESSIBILITY_SOURCE_ID,
+ safetyCenterIssueId,
+ SC_ACCESSIBILITY_ISSUE_TYPE_ID
+ )
+ }
+
+ @Test
+ fun testNotificationClickOpenSafetyCenter() {
+ mAccessibilityServiceRule.enableService()
+ runJobAndWaitUntilCompleted()
+ assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ val statusBarNotification =
+ getNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
+ Assert.assertNotNull(statusBarNotification)
+ val contentIntent = statusBarNotification!!.notification.contentIntent
+ if (SdkLevel.isAtLeastU()) {
+ val options =
+ ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
+ contentIntent.send(
+ /* context = */ null,
+ /* code = */ 0,
+ /* intent = */ null,
+ /* onFinished = */ null,
+ /* handler = */ null,
+ /* requiredPermission = */ null,
+ /* options = */ options.toBundle()
+ )
+ } else {
+ contentIntent.send()
+ }
+ assertSafetyCenterStarted()
+ }
+
+ private fun getAutomation(): UiAutomation {
+ return instrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES
+ )
+ }
+
+ private fun runJobAndWaitUntilCompleted() {
+ TestUtils.runJobAndWaitUntilCompleted(
+ permissionControllerPackage,
+ ACCESSIBILITY_JOB_ID,
+ TIMEOUT_MILLIS,
+ getAutomation()
+ )
+ }
+
+ /** Reset the permission controllers state. */
+ @Throws(Throwable::class)
+ private fun resetPermissionController() {
+ PermissionUtils.resetPermissionControllerJob(
+ getAutomation(),
+ permissionControllerPackage,
+ ACCESSIBILITY_JOB_ID,
+ 45000,
+ ACTION_SET_UP_ACCESSIBILITY_CHECK,
+ AccessibilityOnBootReceiver
+ )
+ }
+
+ companion object {
+ private const val SC_ACCESSIBILITY_SOURCE_ID = "AndroidAccessibility"
+ private const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+ private const val ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled"
+ private const val ACCESSIBILITY_JOB_INTERVAL_MILLIS = "sc_accessibility_job_interval_millis"
+
+ private const val ACCESSIBILITY_JOB_ID = 6
+ private const val ACCESSIBILITY_NOTIFICATION_ID = 4
+ private const val TIMEOUT_MILLIS: Long = 10000
+
+ private const val SC_ACCESSIBILITY_ISSUE_TYPE_ID = "accessibility_privacy_issue"
+
+ private const val AccessibilityOnBootReceiver =
+ "com.android.permissioncontroller.privacysources.AccessibilityOnBootReceiver"
+ private const val ACTION_SET_UP_ACCESSIBILITY_CHECK =
+ "com.android.permissioncontroller.action.SET_UP_ACCESSIBILITY_CHECK"
+
+ @get:ClassRule
+ @JvmStatic
+ val ctsNotificationListenerHelper =
+ CtsNotificationListenerHelperRule(
+ InstrumentationRegistry.getInstrumentation().targetContext
+ )
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/AccessibilityTestService.kt b/tests/cts/permission/src/android/permission/cts/AccessibilityTestService.kt
new file mode 100644
index 000000000..74e8a74ae
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/AccessibilityTestService.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 android.permission.cts
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService
+
+/** Test Accessibility Service */
+class AccessibilityTestService : InstrumentedAccessibilityService()
diff --git a/tests/cts/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java b/tests/cts/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
new file mode 100644
index 000000000..47f011a34
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@AppModeFull(reason = "Tests properties of other app. Instant apps cannot interact with other apps")
+@RunWith(AndroidJUnit4ClassRunner.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class ActivityPermissionRationaleTest {
+ private static final String APK =
+ "/data/local/tmp/cts-permission/CtsAppThatRunsRationaleTests.apk";
+ private static final String PACKAGE_NAME = "android.permission.cts.appthatrunsrationaletests";
+ private static final String PERMISSION_NAME = Manifest.permission.READ_CONTACTS;
+ private static final String CALLBACK_KEY = "testactivitycallback";
+ private static final String RESULT_KEY = "testactivityresult";
+ private static final int TIMEOUT = 5000;
+
+ private static Context sContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private static UiAutomation sUiAuto =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ @BeforeClass
+ public static void setUp() {
+ runShellCommandOrThrow("pm install -r " + APK);
+ int flag = PackageManager.FLAG_PERMISSION_USER_SET;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flag, flag);
+ }
+
+ @AfterClass
+ public static void unInstallApp() {
+ runShellCommand("pm uninstall " + PACKAGE_NAME);
+ }
+
+ private void assertAppShowRationaleIs(boolean expected) throws Exception {
+ CompletableFuture<Boolean> callbackReturn = new CompletableFuture<>();
+ RemoteCallback cb = new RemoteCallback((Bundle result) ->
+ callbackReturn.complete(result.getBoolean(RESULT_KEY)));
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".TestActivity"));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(CALLBACK_KEY, cb);
+
+ sContext.startActivity(intent);
+ assertThat(callbackReturn.get(TIMEOUT, TimeUnit.MILLISECONDS)).isEqualTo(expected);
+ }
+
+ @Before
+ public void clearData() {
+ runShellCommand("pm clear --user " + sContext.getUserId()
+ + " android.permission.cts.appthatrunsrationaletests");
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0);
+ }
+
+ @Test
+ public void permissionGrantedNoRationale() throws Exception {
+ sUiAuto.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void policyFixedNoRationale() throws Exception {
+ int flags = PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void userFixedNoRationale() throws Exception {
+ int flags = PackageManager.FLAG_PERMISSION_USER_FIXED;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void notUserSetNoRationale() throws Exception {
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
+ PackageManager.FLAG_PERMISSION_USER_SET, 0);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void userSetNeedRationale() throws Exception {
+ int flags = PackageManager.FLAG_PERMISSION_USER_SET;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+ assertAppShowRationaleIs(true);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/AppIdleStatePermissionTest.java b/tests/cts/permission/src/android/permission/cts/AppIdleStatePermissionTest.java
new file mode 100644
index 000000000..bba996366
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/AppIdleStatePermissionTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts;
+
+import static org.junit.Assert.fail;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Build, install and run tests with following command:
+ * atest AppIdleStatePermissionTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppIdleStatePermissionTest {
+
+ /**
+ * Verify that the {@link android.Manifest.permission#CHANGE_APP_IDLE_STATE}
+ * permission is only held by at most one package.
+ */
+ @Test
+ public void testChangeAppIdleStatePermission() throws Exception {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[]{
+ android.Manifest.permission.CHANGE_APP_IDLE_STATE
+ }, PackageManager.MATCH_SYSTEM_ONLY);
+
+ int count = 0;
+ String pkgNames = "";
+ for (PackageInfo pkg : holding) {
+ int uid = pm.getApplicationInfo(pkg.packageName, 0).uid;
+ if (UserHandle.isApp(uid)) {
+ pkgNames += pkg.packageName + "\n";
+ count++;
+ }
+ }
+ if (count > 1) {
+ fail("Only one app may hold the CHANGE_APP_IDLE_STATE permission; found packages: \n"
+ + pkgNames);
+ }
+ }
+
+ /**
+ * Verify that the {@link android.Manifest.permission#CHANGE_APP_LAUNCH_TIME_ESTIMATE}
+ * permission is only held by at most one package.
+ */
+ @Test
+ public void testChangeAppLaunchEstimatePermission() throws Exception {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[]{
+ android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE
+ }, PackageManager.MATCH_SYSTEM_ONLY);
+
+ int count = 0;
+ String pkgNames = "";
+ for (PackageInfo pkg : holding) {
+ int uid = pm.getApplicationInfo(pkg.packageName, 0).uid;
+ if (UserHandle.isApp(uid)) {
+ pkgNames += pkg.packageName + "\n";
+ count++;
+ }
+ }
+ if (count > 1) {
+ fail("Only one app may hold the CHANGE_APP_LAUNCH_TIME_ESTIMATE permission;"
+ + " found packages: \n" + pkgNames);
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/cts/permission/src/android/permission/cts/AppWidgetManagerPermissionTest.java b/tests/cts/permission/src/android/permission/cts/AppWidgetManagerPermissionTest.java
new file mode 100644
index 000000000..fa43bfb18
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/AppWidgetManagerPermissionTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+* Test that protected AppWidgetManager APIs cannot be called without permissions
+*/
+public class AppWidgetManagerPermissionTest extends AndroidTestCase {
+
+ private AppWidgetManager mAppWidgetManager = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAppWidgetManager = AppWidgetManager.getInstance(getContext());
+ }
+
+ /**
+ * Verify that calling
+ * {@link AppWidgetManager#bindAppWidgetId(int, android.content.ComponentName)}
+ * requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#BIND_APP_WIDGET}.
+ */
+ @SmallTest
+ public void testBindAppWidget() {
+ if (!getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ return;
+ }
+ assertNotNull(mAppWidgetManager);
+
+ try {
+ final boolean bound = mAppWidgetManager.bindAppWidgetIdIfAllowed(1,
+ new ComponentName(mContext, "foo"));
+ assertFalse("Was able to call bindAppWidgetId", bound);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionButtonLabelTest.java b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionButtonLabelTest.java
new file mode 100644
index 000000000..004f8bc8c
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionButtonLabelTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BackgroundPermissionButtonLabelTest {
+
+ // Name of the resource which provides background permission button string
+ public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
+ "app_permission_button_allow_always";
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final String mPermissionController =
+ mContext.getPackageManager().getPermissionControllerPackageName();
+
+ @Test
+ public void testBackgroundPermissionButtonLabel() {
+ try {
+ Context permissionControllerContext =
+ mContext.createPackageContext(mPermissionController, 0);
+ int stringId = permissionControllerContext.getResources().getIdentifier(
+ APP_PERMISSION_BUTTON_ALLOW_ALWAYS, "string",
+ "com.android.permissioncontroller");
+
+ Assert.assertEquals(mContext.getPackageManager().getBackgroundPermissionOptionLabel(),
+ permissionControllerContext.getString(stringId));
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+}
diff --git a/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
new file mode 100644
index 000000000..f3f47631c
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/BackgroundPermissionsTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.content.pm.PermissionInfo.PROTECTION_INTERNAL;
+import static android.permission.cts.PermissionUtils.getAppOp;
+import static android.permission.cts.PermissionUtils.grantPermission;
+import static android.permission.cts.PermissionUtils.install;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.AppOpsManager;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BackgroundPermissionsTest {
+ private static final String LOG_TAG = BackgroundPermissionsTest.class.getSimpleName();
+
+ /** The package name of all apps used in the test */
+ private static final String APP_PKG = "android.permission.cts.appthatrequestpermission";
+
+ private static final String TMP_DIR = "/data/local/tmp/cts-permission/";
+ private static final String APK_LOCATION_BACKGROUND_29 =
+ TMP_DIR + "CtsAppThatRequestsLocationAndBackgroundPermission29.apk";
+ private static final String APK_LOCATION_29v4 =
+ TMP_DIR + "CtsAppThatRequestsLocationPermission29v4.apk";
+
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private static final UiAutomation sUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ @After
+ public void uninstallTestApp() {
+ uninstallApp(APP_PKG);
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages")
+ public void verifybackgroundPermissionsProperties() throws Exception {
+ PackageInfo pkg = sContext.getPackageManager().getPackageInfo(
+ "android", PackageManager.GET_PERMISSIONS);
+ ArrayMap<String, String> potentialBackgroundPermissionsToGroup = new ArrayMap<>();
+
+ int numPermissions = pkg.permissions.length;
+ for (int i = 0; i < numPermissions; i++) {
+ PermissionInfo permission = pkg.permissions[i];
+
+ // background permissions must be dangerous or ungrantable or role
+ if ((permission.getProtection() & PROTECTION_DANGEROUS) != 0
+ || (permission.getProtection() == PROTECTION_INTERNAL
+ && (permission.getProtectionFlags() == 0
+ || permission.getProtectionFlags() == PermissionInfo.PROTECTION_FLAG_ROLE))) {
+ potentialBackgroundPermissionsToGroup.put(permission.name, permission.group);
+ }
+ }
+
+ for (int i = 0; i < numPermissions; i++) {
+ PermissionInfo permission = pkg.permissions[i];
+ String backgroundPermissionName = permission.backgroundPermission;
+
+ if (backgroundPermissionName != null) {
+ Log.i(LOG_TAG, permission.name + "->" + backgroundPermissionName);
+
+ // foreground permissions must be dangerous
+ assertNotEquals(0, permission.getProtection() & PROTECTION_DANGEROUS);
+
+ // All foreground permissions need an app op
+ assertNotNull(AppOpsManager.permissionToOp(permission.name));
+
+ // the background permission must exist
+ assertTrue(potentialBackgroundPermissionsToGroup
+ .containsKey(backgroundPermissionName));
+ }
+ }
+ }
+
+ /**
+ * If a bg permission is lost during an upgrade, the app-op should downgrade to foreground
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpGetsDowngradedWhenBgPermIsNotRequestedAnymore() throws Exception {
+ install(APK_LOCATION_BACKGROUND_29);
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ install(APK_LOCATION_29v4);
+
+ eventually(() -> assertWithMessage("foreground app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
+ }
+
+ /**
+ * Make sure location switch-op is set if no location access is granted.
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfNoLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+
+ // Wait until the system sets the app-op automatically
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_IGNORED));
+ }
+
+ /**
+ * Make sure location switch-op is set if only coarse location is granted
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfOnlyCoarseLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ // Wait until the system sets the app-op automatically
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
+ }
+
+ /**
+ * Make sure location switch-op is set if coarse location with background access is granted.
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfCoarseAndBgLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ // Wait until the system sets the app-op automatically
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_ALLOWED));
+ }
+
+ /**
+ * Make sure location switch-op is set if only fine location is granted
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfOnlyFineLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
+
+ // Wait until the system sets the app-op automatically
+ // Fine location uses background location to limit access
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
+ }
+
+ /**
+ * Make sure location switch-op is set if fine location with background access is granted.
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfFineAndBgLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ // Wait until the system sets the app-op automatically
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_ALLOWED));
+ }
+
+ /**
+ * Make sure location switch-op is set if fine and coarse location access is granted.
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfFineAndCoarseLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ // Wait until the system sets the app-op automatically
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
+ }
+
+ /**
+ * Make sure location switch-op is set if fine and coarse location with background access is
+ * granted.
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them. Also instant apps are never updated, hence the test "
+ + "is useless.")
+ public void appOpIsSetIfFineCoarseAndBgLocPermIsGranted() {
+ install(APK_LOCATION_BACKGROUND_29);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_FINE_LOCATION);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ sUiAutomation.grantRuntimePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ // Wait until the system sets the app-op automatically
+ eventually(() -> assertWithMessage("loc app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_ALLOWED));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java b/tests/cts/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
new file mode 100644
index 000000000..a4551f610
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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 android.permission.cts;
+
+import static android.os.Process.myUserHandle;
+import static android.permission.cts.TestUtils.eventually;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.app.NotificationManager;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.DeviceConfig;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+
+import java.util.List;
+
+/**
+ * Base test class used for {@code NotificationListenerCheckTest} and
+ * {@code NotificationListenerCheckWithSafetyCenterUnsupportedTest}
+ */
+public class BaseNotificationListenerCheckTest {
+ private static final String LOG_TAG = BaseNotificationListenerCheckTest.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ protected static final String TEST_APP_PKG =
+ "android.permission.cts.appthathasnotificationlistener";
+ private static final String TEST_APP_NOTIFICATION_SERVICE =
+ TEST_APP_PKG + ".CtsNotificationListenerService";
+ protected static final String TEST_APP_NOTIFICATION_LISTENER_APK =
+ "/data/local/tmp/cts-permission/CtsAppThatHasNotificationListener.apk";
+
+ private static final int NOTIFICATION_LISTENER_CHECK_JOB_ID = 4;
+
+ /**
+ * Device config property for whether notification listener check is enabled on the device
+ */
+ private static final String PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED =
+ "notification_listener_check_enabled";
+
+ /**
+ * Device config property for time period in milliseconds after which current enabled
+ * notification
+ * listeners are queried
+ */
+ protected static final String PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS =
+ "notification_listener_check_interval_millis";
+
+ protected static final Long OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS =
+ SECONDS.toMillis(0);
+
+ private static final String ACTION_SET_UP_NOTIFICATION_LISTENER_CHECK =
+ "com.android.permissioncontroller.action.SET_UP_NOTIFICATION_LISTENER_CHECK";
+ private static final String NotificationListenerOnBootReceiver =
+ "com.android.permissioncontroller.privacysources.SetupPeriodicNotificationListenerCheck";
+
+ /**
+ * ID for notification shown by
+ * {@link com.android.permissioncontroller.privacysources.NotificationListenerCheck}.
+ */
+ public static final int NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID = 3;
+
+ protected static final long UNEXPECTED_TIMEOUT_MILLIS = 10000;
+ protected static final long ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS = 5000;
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final PackageManager sPackageManager = sContext.getPackageManager();
+ private static final UiAutomation sUiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+
+ private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager()
+ .getPermissionControllerPackageName();
+
+ private static List<ComponentName> sPreviouslyEnabledNotificationListeners;
+
+ // Override SafetyCenter enabled flag
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigSafetyCenterEnabled =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterUtils.PROPERTY_SAFETY_CENTER_ENABLED,
+ Boolean.toString(true));
+
+ // Override NlsCheck enabled flag
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigNlsCheckEnabled =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
+ Boolean.toString(true));
+
+ // Override general notification interval from once every day to once ever 1 second
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigNlsCheckIntervalMillis =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS,
+ Long.toString(OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS));
+
+ @Rule
+ public CtsNotificationListenerHelperRule ctsNotificationListenerHelper =
+ new CtsNotificationListenerHelperRule(sContext);
+
+ @BeforeClass
+ public static void beforeClassSetup() throws Exception {
+ // Disallow any OEM enabled NLS
+ disallowPreexistingNotificationListeners();
+ }
+
+ @AfterClass
+ public static void afterClassTearDown() throws Throwable {
+ // Reallow any previously OEM allowed NLS
+ reallowPreexistingNotificationListeners();
+ }
+
+ protected static void setDeviceConfigPrivacyProperty(String propertyName, String value) {
+ runWithShellPermissionIdentity(() -> {
+ boolean valueWasSet = DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ /* name = */ propertyName,
+ /* value = */ value,
+ /* makeDefault = */ false);
+ if (!valueWasSet) {
+ throw new IllegalStateException("Could not set " + propertyName + " to " + value);
+ }
+ });
+ }
+
+ /**
+ * Enable or disable notification listener check
+ */
+ protected static void setNotificationListenerCheckEnabled(boolean enabled) {
+ setDeviceConfigPrivacyProperty(
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
+ /* value = */ String.valueOf(enabled));
+ }
+
+ /**
+ * Allow or disallow a {@link NotificationListenerService} component for the current user
+ *
+ * @param listenerComponent {@link NotificationListenerService} component to allow or disallow
+ */
+ private static void setNotificationListenerServiceAllowed(ComponentName listenerComponent,
+ boolean allowed) {
+ String command = " cmd notification " + (allowed ? "allow_listener " : "disallow_listener ")
+ + listenerComponent.flattenToString();
+ runShellCommand(command);
+ }
+
+ private static void disallowPreexistingNotificationListeners() {
+ runWithShellPermissionIdentity(() -> {
+ NotificationManager notificationManager =
+ sContext.getSystemService(NotificationManager.class);
+ sPreviouslyEnabledNotificationListeners =
+ notificationManager.getEnabledNotificationListeners();
+ });
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Found " + sPreviouslyEnabledNotificationListeners.size()
+ + " previously allowed notification listeners. Disabling before test run.");
+ }
+ for (ComponentName listener : sPreviouslyEnabledNotificationListeners) {
+ setNotificationListenerServiceAllowed(listener, false);
+ }
+ }
+
+ private static void reallowPreexistingNotificationListeners() {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Re-allowing " + sPreviouslyEnabledNotificationListeners.size()
+ + " previously allowed notification listeners found before test run.");
+ }
+ for (ComponentName listener : sPreviouslyEnabledNotificationListeners) {
+ setNotificationListenerServiceAllowed(listener, true);
+ }
+ }
+
+ protected void allowTestAppNotificationListenerService() {
+ setNotificationListenerServiceAllowed(
+ new ComponentName(TEST_APP_PKG, TEST_APP_NOTIFICATION_SERVICE), true);
+ }
+
+ protected void disallowTestAppNotificationListenerService() {
+ setNotificationListenerServiceAllowed(
+ new ComponentName(TEST_APP_PKG, TEST_APP_NOTIFICATION_SERVICE), false);
+ }
+
+ /**
+ * Force a run of the notification listener check.
+ */
+ protected static void runNotificationListenerCheck() throws Throwable {
+ TestUtils.awaitJobUntilRequestedState(
+ PERMISSION_CONTROLLER_PKG,
+ NOTIFICATION_LISTENER_CHECK_JOB_ID,
+ UNEXPECTED_TIMEOUT_MILLIS,
+ sUiAutomation,
+ "waiting"
+ );
+
+ TestUtils.runJobAndWaitUntilCompleted(
+ PERMISSION_CONTROLLER_PKG,
+ NOTIFICATION_LISTENER_CHECK_JOB_ID,
+ UNEXPECTED_TIMEOUT_MILLIS,
+ sUiAutomation
+ );
+ }
+
+ /**
+ * Skip tests for if Safety Center not supported
+ */
+ protected void assumeDeviceSupportsSafetyCenter() {
+ assumeTrue(SafetyCenterUtils.deviceSupportsSafetyCenter(sContext));
+ }
+
+ /**
+ * Skip tests for if Safety Center IS supported
+ */
+ protected void assumeDeviceDoesNotSupportSafetyCenter() {
+ assumeFalse(SafetyCenterUtils.deviceSupportsSafetyCenter(sContext));
+ }
+
+ protected void wakeUpAndDismissKeyguard() {
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ runShellCommand("wm dismiss-keyguard");
+ }
+
+ /**
+ * Reset the permission controllers state before each test
+ */
+ protected void resetPermissionControllerBeforeEachTest() throws Throwable {
+ resetPermissionController();
+
+ // ensure no posted notification listener notifications exits
+ eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ // Reset job scheduler stats (to allow more jobs to be run)
+ runShellCommand(
+ "cmd jobscheduler reset-execution-quota -u " + myUserHandle().getIdentifier() + " "
+ + PERMISSION_CONTROLLER_PKG);
+ runShellCommand("cmd jobscheduler reset-schedule-quota");
+ }
+
+ /**
+ * Reset the permission controllers state.
+ */
+ private static void resetPermissionController() throws Throwable {
+ PermissionUtils.resetPermissionControllerJob(sUiAutomation, PERMISSION_CONTROLLER_PKG,
+ NOTIFICATION_LISTENER_CHECK_JOB_ID, 45000,
+ ACTION_SET_UP_NOTIFICATION_LISTENER_CHECK, NotificationListenerOnBootReceiver);
+ }
+
+ /**
+ * Preshow/dismiss cts NotificationListener notification as it negatively affects test results
+ * (can result in unexpected test pass/failures)
+ */
+ protected void triggerAndDismissCtsNotificationListenerNotification() throws Throwable {
+ // CtsNotificationListenerService isn't enabled at this point, but NotificationListener
+ // should be. Mark as notified by showing and dismissing
+ runNotificationListenerCheck();
+
+ // Ensure notification shows and dismiss
+ eventually(() -> assertNotNull(getNotification(true)),
+ UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * Get a notification listener notification that is currently visible.
+ *
+ * @param cancelNotification if `true` the notification is canceled inside this method
+ * @return The notification or `null` if there is none
+ */
+ protected StatusBarNotification getNotification(boolean cancelNotification) throws Throwable {
+ return CtsNotificationListenerServiceUtils.getNotificationForPackageAndId(
+ PERMISSION_CONTROLLER_PKG,
+ NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID,
+ cancelNotification);
+ }
+
+ /**
+ * Clear any notifications related to NotificationListenerCheck to ensure clean test setup
+ */
+ protected void clearNotifications() throws Throwable {
+ // Clear notification if present
+ CtsNotificationListenerServiceUtils.cancelNotifications(PERMISSION_CONTROLLER_PKG);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/Camera2PermissionTest.java b/tests/cts/permission/src/android/permission/cts/Camera2PermissionTest.java
new file mode 100644
index 000000000..379f47815
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/Camera2PermissionTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCharacteristics.Key;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import com.android.ex.camera2.blocking.BlockingCameraManager;
+import com.android.ex.camera2.blocking.BlockingStateCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for Camera2 API related Permissions. Currently, this means
+ * android.permission.CAMERA.
+ */
+public class Camera2PermissionTest extends AndroidTestCase {
+ private static final String TAG = "Camera2PermissionTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
+
+ private CameraManager mCameraManager;
+ private CameraDevice mCamera;
+ private BlockingStateCallback mCameraListener;
+ private String[] mCameraIds;
+ protected Handler mHandler;
+ protected HandlerThread mHandlerThread;
+
+ @Override
+ public void setContext(Context context) {
+ super.setContext(context);
+ mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ assertNotNull("Can't connect to camera manager!", mCameraManager);
+ }
+
+ /**
+ * Set up the camera2 test case required environments, including CameraManager,
+ * HandlerThread, Camera IDs, and CameraStateCallback etc.
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCameraIds = mCameraManager.getCameraIdList();
+ assertNotNull("Camera ids shouldn't be null", mCameraIds);
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mCameraListener = new BlockingStateCallback();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mHandlerThread.quitSafely();
+ mHandler = null;
+
+ super.tearDown();
+ }
+
+ /**
+ * Attempt to open camera. Requires Permission:
+ * {@link android.Manifest.permission#CAMERA}.
+ */
+ public void testCameraOpen() throws Exception {
+ for (String id : mCameraIds) {
+ try {
+ openCamera(id);
+ fail("Was able to open camera " + id + " with no permission");
+ }
+ catch (SecurityException e) {
+ // expected
+ } finally {
+ closeCamera();
+ }
+ }
+ }
+
+ /**
+ * Check that no system cameras can be discovered without
+ * {@link android.Manifest.permission#CAMERA} and android.permission.SYSTEM_CAMERA
+ */
+ public void testSystemCameraDiscovery() throws Exception {
+ for (String id : mCameraIds) {
+ Log.i(TAG, "testSystemCameraDiscovery for camera id " + id);
+ CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
+ assertNotNull("Camera characteristics shouldn't be null", characteristics);
+ int[] availableCapabilities =
+ characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ assertTrue("Camera capabilities shouldn't be null", availableCapabilities != null);
+ List<Integer> capList = toList(availableCapabilities);
+ assertFalse("System camera device " + id + " should not be public",
+ capList.contains(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA));
+ }
+ }
+
+ /**
+ * Check the absence of camera characteristics keys that require Permission:
+ * {@link android.Manifest.permission#CAMERA}.
+ */
+ public void testCameraCharacteristicsNeedingPermission() throws Exception {
+ for (String id : mCameraIds) {
+ CameraCharacteristics capabilities = mCameraManager.getCameraCharacteristics(id);
+ assertNotNull("Camera characteristics shouldn't be null", capabilities);
+ List<Key<?>> keysNeedingPermission = capabilities.getKeysNeedingPermission();
+ if (keysNeedingPermission == null) {
+ continue;
+ }
+ List<Key<?>> keys = capabilities.getKeys();
+ assertNotNull("Camera characteristics key list shouldn't be null", keys);
+ for (Key<?> key : keysNeedingPermission) {
+ assertEquals("Key " + key.getName() + " needing permission is part of the" +
+ " available characteristics keys", -1, keys.indexOf(key));
+ assertNull("Key " + key.getName() + " needing permission must not present" +
+ " in camera characteristics", capabilities.get(key));
+ }
+ }
+ }
+
+ /**
+ * Add and remove availability listeners should work without permission.
+ */
+ @Presubmit
+ public void testAvailabilityCallback() throws Exception {
+ DummyCameraListener availabilityListener = new DummyCameraListener();
+ // Remove a not-registered listener is a no-op.
+ mCameraManager.unregisterAvailabilityCallback(availabilityListener);
+ mCameraManager.registerAvailabilityCallback(availabilityListener, mHandler);
+ mCameraManager.unregisterAvailabilityCallback(availabilityListener);
+ mCameraManager.registerAvailabilityCallback(availabilityListener, mHandler);
+ mCameraManager.registerAvailabilityCallback(availabilityListener, mHandler);
+ mCameraManager.unregisterAvailabilityCallback(availabilityListener);
+ // Remove a previously-added listener second time is a no-op.
+ mCameraManager.unregisterAvailabilityCallback(availabilityListener);
+ }
+
+ private class DummyCameraListener extends CameraManager.AvailabilityCallback {
+ @Override
+ public void onCameraAvailable(String cameraId) {
+ }
+
+ @Override
+ public void onCameraUnavailable(String cameraId) {
+ }
+ }
+
+ private void openCamera(String cameraId) throws Exception {
+ mCamera = (new BlockingCameraManager(mCameraManager)).openCamera(
+ cameraId, mCameraListener, mHandler);
+ }
+
+ private static List<Integer> toList(int[] array) {
+ List<Integer> list = new ArrayList<Integer>();
+ for (int i : array) {
+ list.add(i);
+ }
+ return list;
+ }
+
+ private void closeCamera() {
+ if (mCamera != null) {
+ mCamera.close();
+ mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ mCamera = null;
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/CameraPermissionTest.java b/tests/cts/permission/src/android/permission/cts/CameraPermissionTest.java
new file mode 100644
index 000000000..981735388
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/CameraPermissionTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.hardware.Camera;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.MediumTest;
+
+import java.io.FileOutputStream;
+
+/**
+ * Tests for camera-related Permissions. Currently, this means
+ * android.permission.CAMERA.
+ */
+public class CameraPermissionTest extends AndroidTestCase {
+
+ private static String PATH_PREFIX = Environment.getExternalStorageDirectory().toString();
+ private static String CAMERA_IMAGE_PATH = PATH_PREFIX + "this-should-not-exist.jpg";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ class ShutterCallback implements Camera.ShutterCallback {
+ public void onShutter() { }
+ }
+
+ class RawPictureCallback implements Camera.PictureCallback {
+ public void onPictureTaken(byte [] rawData, Camera camera) { }
+ }
+
+ class JpegPictureCallback implements Camera.PictureCallback {
+ public void onPictureTaken(byte [] jpegData, Camera camera) {
+ if (jpegData == null) {
+ // TODO: Is this good (= expected, = correct), or weird, or bad?
+ return;
+ }
+
+ try {
+ FileOutputStream s = new FileOutputStream(CAMERA_IMAGE_PATH);
+ s.write(jpegData);
+ s.flush();
+ }
+ catch (SecurityException e) {
+ // Sure, NOW they tell us (NOTE: this could be a side effect
+ // of the upcoming WRITE_EXTERNAL_STORAGE permission).
+ } catch (Exception e) {
+ // We didn't really need to save it anyway, did we?
+ }
+
+ fail("Successfully captured an image of " + jpegData.length +
+ " bytes, and saved it to " + CAMERA_IMAGE_PATH);
+ }
+ }
+
+ /**
+ * Attempt to take a picture. Requires Permission:
+ * {@link android.Manifest.permission#CAMERA}.
+ */
+ @MediumTest
+ public void testCamera() {
+ try {
+ (Camera.open()).takePicture(new ShutterCallback(),
+ new RawPictureCallback(),
+ new JpegPictureCallback());
+ fail("Was able to take a picture with the camera with no permission");
+ }
+ catch (SecurityException e) {
+ // expected
+ } catch (RuntimeException e) {
+ // expected
+ // The JNI layer isn't translating the EPERM error status into
+ // a SecurityException.
+ }
+ }
+
+}
+
diff --git a/tests/cts/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java b/tests/cts/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
new file mode 100644
index 000000000..0e8797d20
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.OemNetworkPreferences;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+/**
+* Test that protected android.net.ConnectivityManager methods cannot be called without
+* permissions
+*/
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityManagerPermissionTest {
+
+ private ConnectivityManager mConnectivityManager;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
+ assertNotNull(mConnectivityManager);
+ }
+
+ /**
+ * Verify that calling {@link ConnectivityManager#getNetworkInfo(int))}
+ * requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ */
+ @Test
+ public void testGetNetworkInfo() {
+ try {
+ mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+ fail("Was able to call getNetworkInfo");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that calling {@link ConnectivityManager#setOemNetworkPreference(OemNetworkPreferences,
+ * Executor, ConnectivityManager.OnSetOemNetworkPreferenceListener)}
+ * requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#CONTROL_OEM_PAID_NETWORK_PREFERENCE}.
+ */
+ @Test
+ public void testSetOemNetworkPreference() {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE));
+ try {
+ final String testPackage = "does.not.matter.com";
+ final int testPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final OemNetworkPreferences preferences =
+ new OemNetworkPreferences.Builder()
+ .addNetworkPreference(testPackage, testPref)
+ .build();
+ mConnectivityManager.setOemNetworkPreference(preferences, null, null);
+ fail("Was able to call setOemNetworkPreference");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that calling {@link ConnectivityManager#setOemNetworkPreference(OemNetworkPreferences,
+ * Executor, ConnectivityManager.OnSetOemNetworkPreferenceListener)}
+ * requires automotive feature.
+ * <p>Tests Feature:
+ * {@link PackageManager#FEATURE_AUTOMOTIVE}.
+ */
+ @Test
+ public void testSetOemNetworkPreferenceHasAutomotiveFeature() {
+ assumeFalse(mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE));
+ try {
+ final String testPackage = "does.not.matter.com";
+ final int testPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final OemNetworkPreferences preferences =
+ new OemNetworkPreferences.Builder()
+ .addNetworkPreference(testPackage, testPref)
+ .build();
+ mConnectivityManager.setOemNetworkPreference(preferences, null, null);
+ fail("Was able to call setOemNetworkPreference");
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/ContactsProviderTest.java b/tests/cts/permission/src/android/permission/cts/ContactsProviderTest.java
new file mode 100644
index 000000000..69b64d790
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ContactsProviderTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify permissions are enforced.
+ */
+public class ContactsProviderTest extends AndroidTestCase {
+
+ /**
+ * Verifies that query(ContactsContract.Contacts.CONTENT_URI) requires
+ * Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#READ_CONTACTS}.
+ */
+ @SmallTest
+ public void testQueryContacts() {
+ try {
+ getContext().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+ null, null, null, null);
+ fail("query(ContactsContract.Contacts.CONTENT_URI) did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that insert(ContactsContract.Contacts.CONTENT_URI) requires
+ * Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#WRITE_CONTACTS}.
+ */
+ @SmallTest
+ public void testInsertContacts() {
+ try {
+ getContext().getContentResolver().insert(ContactsContract.Contacts.CONTENT_URI,
+ new ContentValues());
+ fail("insert(ContactsContract.Contacts.CONTENT_URI) did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that query(ContactsContract.Profile.CONTENT_URI) requires
+ * Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#READ_CONTACTS}.
+ */
+ @SmallTest
+ public void testQueryProfile() {
+ try {
+ getContext().getContentResolver().query(ContactsContract.Profile.CONTENT_URI,
+ null, null, null, null);
+ fail("query(ContactsContract.Profile.CONTENT_URI) did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that insert(ContactsContract.Profile.CONTENT_URI) requires
+ * Permission. The provider doesn't actually let you do this even if you have the
+ * permission, but trying to do it without the permission should throw a
+ * SecurityException anyway.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#WRITE_CONTACTS}.
+ */
+ @SmallTest
+ public void testInsertProfile() {
+ try {
+ getContext().getContentResolver().insert(ContactsContract.Profile.CONTENT_URI,
+ new ContentValues(0));
+ fail("insert(ContactsContract.Profile.CONTENT_URI) did not throw SecurityException "
+ + "as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that update(ContactsContract.Profile.CONTENT_URI) requires
+ * Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#WRITE_CONTACTS}.
+ */
+ @SmallTest
+ public void testUpdateProfile() {
+ try {
+ getContext().getContentResolver().update(ContactsContract.Profile.CONTENT_URI,
+ new ContentValues(0), null, null);
+ fail("update(ContactsContract.Profile.CONTENT_URI) did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that query(ContactsContract.CommonDataKinds.Phone.ENTERPRISE_CONTENT_URI) requires
+ * Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
+ */
+ @SmallTest
+ public void testQueryPhoneEnterprise() {
+ try {
+ getContext().getContentResolver().query(
+ ContactsContract.CommonDataKinds.Phone.ENTERPRISE_CONTENT_URI,
+ null, null, null, null);
+ fail("query(ContactsContract.CommonDataKinds.Phone.ENTERPRISE_CONTENT_URI) did not"
+ + " throw SecurityException as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that query(ContactsContract.RawContactsEntity.CORP_CONTENT_URI) requires
+ * Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
+ */
+ @SmallTest
+ public void testRawContactsEntityCorp() {
+ try {
+ getContext().getContentResolver().query(
+ ContactsContract.RawContactsEntity.CORP_CONTENT_URI, null, null, null, null);
+ fail("query(ContactsContract.RawContactsEntity.CORP_CONTENT_URI) did not throw"
+ + " SecurityException as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/DebuggableTest.java b/tests/cts/permission/src/android/permission/cts/DebuggableTest.java
new file mode 100644
index 000000000..7ca734887
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/DebuggableTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.test.AndroidTestCase;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Verify that pre-installed packages don't have the debuggable
+ * flag set. The debuggable flag allows should only be used during
+ * development, and never for shipping devices.
+ */
+public class DebuggableTest extends AndroidTestCase {
+
+ public void testNoDebuggable() {
+ Set<String> debuggableApps = new HashSet<String>();
+ List<ApplicationInfo> apps = getContext()
+ .getPackageManager()
+ .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
+ for (ApplicationInfo app : apps) {
+ if (!app.isSystemApp()) {
+ continue;
+ }
+ String appName = app.packageName;
+ if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE) {
+ debuggableApps.add(appName);
+ }
+ }
+ assertTrue("Packages marked debuggable: " + debuggableApps, debuggableApps.isEmpty());
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/DevicePermissionsTest.kt b/tests/cts/permission/src/android/permission/cts/DevicePermissionsTest.kt
new file mode 100644
index 000000000..dbda1f5e1
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/DevicePermissionsTest.kt
@@ -0,0 +1,562 @@
+/*
+ * 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 android.permission.cts
+
+import android.Manifest
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
+import android.app.Instrumentation
+import android.companion.virtual.VirtualDeviceManager
+import android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+import android.companion.virtual.VirtualDeviceManager.VirtualDevice
+import android.companion.virtual.VirtualDeviceParams
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
+import android.content.pm.PackageManager.PERMISSION_DENIED
+import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.os.Build
+import android.os.UserHandle
+import android.permission.PermissionManager
+import android.permission.flags.Flags
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.virtualdevice.cts.common.FakeAssociationRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.AdoptShellPermissionsRule
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.android.compatibility.common.util.SystemUtil.waitForBroadcasts
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume.assumeNotNull
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@AppModeFull(reason = " cannot be accessed by instant apps")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+class DevicePermissionsTest {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val defaultDeviceContext = instrumentation.targetContext
+
+ private lateinit var virtualDevice: VirtualDevice
+ private lateinit var virtualDeviceContext: Context
+ private lateinit var persistentDeviceId: String
+
+ private lateinit var permissionManager: PermissionManager
+
+ @get:Rule var mFakeAssociationRule = FakeAssociationRule()
+
+ @get:Rule
+ val mAdoptShellPermissionsRule =
+ AdoptShellPermissionsRule(
+ instrumentation.uiAutomation,
+ Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ Manifest.permission.GET_RUNTIME_PERMISSIONS
+ )
+
+ @Rule @JvmField val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Before
+ fun setup() {
+ val virtualDeviceManager =
+ defaultDeviceContext.getSystemService(VirtualDeviceManager::class.java)
+ assumeNotNull(virtualDeviceManager)
+ virtualDevice =
+ virtualDeviceManager!!.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ VirtualDeviceParams.Builder().build()
+ )
+ virtualDeviceContext = defaultDeviceContext.createDeviceContext(virtualDevice.deviceId)
+ permissionManager = virtualDeviceContext.getSystemService(PermissionManager::class.java)!!
+ persistentDeviceId = virtualDevice.persistentDeviceId!!
+ runShellCommandOrThrow("pm install -r $TEST_APK")
+ }
+
+ @After
+ fun cleanup() {
+ runShellCommandOrThrow("pm uninstall $TEST_PACKAGE_NAME")
+ if (this::virtualDevice.isInitialized) {
+ virtualDevice.close()
+ }
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testDeviceAwareRuntimePermissionIsGranted() {
+ grantPermissionAndAssertGranted(Manifest.permission.CAMERA, virtualDeviceContext)
+ }
+
+ @RequiresFlagsDisabled(Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED)
+ @Test
+ fun testDeviceAwareRuntimePermissionGrantIsInherited() {
+ grantPermissionAndAssertGranted(Manifest.permission.CAMERA, defaultDeviceContext)
+
+ assertPermission(Manifest.permission.CAMERA, PERMISSION_GRANTED, virtualDeviceContext)
+ }
+
+ @Test
+ fun testNonDeviceAwareRuntimePermissionGrantIsInherited() {
+ grantPermissionAndAssertGranted(NON_DEVICE_AWARE_PERMISSION, defaultDeviceContext)
+
+ assertPermission(NON_DEVICE_AWARE_PERMISSION, PERMISSION_GRANTED, virtualDeviceContext)
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testDeviceAwareRuntimePermissionIsRevoked() {
+ grantPermissionAndAssertGranted(DEVICE_AWARE_PERMISSION, virtualDeviceContext)
+
+ revokePermissionAndAssertDenied(DEVICE_AWARE_PERMISSION, virtualDeviceContext)
+ }
+
+ @Test
+ fun testNonDeviceAwareRuntimePermissionIsRevokedForDefaultDevice() {
+ grantPermissionAndAssertGranted(NON_DEVICE_AWARE_PERMISSION, defaultDeviceContext)
+ assertPermission(NON_DEVICE_AWARE_PERMISSION, PERMISSION_GRANTED, virtualDeviceContext)
+ // Revoke call from virtualDeviceContext should revoke for default device as well.
+ revokePermissionAndAssertDenied(NON_DEVICE_AWARE_PERMISSION, virtualDeviceContext)
+ assertPermission(NON_DEVICE_AWARE_PERMISSION, PERMISSION_DENIED, defaultDeviceContext)
+ }
+
+ @Test
+ fun testNormalPermissionGrantIsInherited() {
+ assertPermission(Manifest.permission.INTERNET, PERMISSION_GRANTED, virtualDeviceContext)
+ }
+
+ @Test
+ fun testSignaturePermissionGrantIsInherited() {
+ assertPermission(CUSTOM_SIGNATURE_PERMISSION, PERMISSION_GRANTED, virtualDeviceContext)
+ }
+
+ @Test
+ fun testOneTimePermissionIsRevoked() {
+ grantPermissionAndAssertGranted(DEVICE_AWARE_PERMISSION, virtualDeviceContext)
+ virtualDeviceContext.packageManager.updatePermissionFlags(
+ DEVICE_AWARE_PERMISSION,
+ TEST_PACKAGE_NAME,
+ FLAG_PERMISSION_ONE_TIME,
+ FLAG_PERMISSION_ONE_TIME,
+ UserHandle.of(virtualDeviceContext.userId)
+ )
+
+ permissionManager.startOneTimePermissionSession(
+ TEST_PACKAGE_NAME,
+ 0,
+ 0,
+ IMPORTANCE_FOREGROUND,
+ IMPORTANCE_FOREGROUND_SERVICE
+ )
+ eventually {
+ assertPermission(DEVICE_AWARE_PERMISSION, PERMISSION_DENIED, virtualDeviceContext)
+ }
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testRevokeSelfPermissionOnKill() {
+ grantPermissionAndAssertGranted(DEVICE_AWARE_PERMISSION, virtualDeviceContext)
+
+ revokeSelfPermission(DEVICE_AWARE_PERMISSION, virtualDeviceContext)
+ eventually {
+ assertPermission(DEVICE_AWARE_PERMISSION, PERMISSION_DENIED, virtualDeviceContext)
+ }
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testGrantAndRevokeDeviceAwarePermissionByPersistentDeviceId() {
+ val deviceAwarePermission = DEVICE_AWARE_PERMISSION
+
+ permissionManager.grantRuntimePermission(
+ TEST_PACKAGE_NAME,
+ deviceAwarePermission,
+ persistentDeviceId
+ )
+
+ assertThat(
+ permissionManager.checkPermission(
+ deviceAwarePermission,
+ TEST_PACKAGE_NAME,
+ virtualDevice.persistentDeviceId!!
+ )
+ )
+ .isEqualTo(PERMISSION_GRANTED)
+
+ assertThat(
+ permissionManager.checkPermission(
+ deviceAwarePermission,
+ TEST_PACKAGE_NAME,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ )
+ )
+ .isEqualTo(PERMISSION_DENIED)
+
+ permissionManager.revokeRuntimePermission(
+ TEST_PACKAGE_NAME,
+ deviceAwarePermission,
+ persistentDeviceId,
+ "test"
+ )
+
+ assertThat(
+ permissionManager.checkPermission(
+ deviceAwarePermission,
+ TEST_PACKAGE_NAME,
+ virtualDevice.persistentDeviceId!!
+ )
+ )
+ .isEqualTo(PERMISSION_DENIED)
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testUpdateAndGetPermissionFlagsByPersistentDeviceId() {
+ val deviceAwarePermission = DEVICE_AWARE_PERMISSION
+ val flagMask = FLAG_PERMISSION_USER_SET or FLAG_PERMISSION_USER_FIXED
+ val flag = FLAG_PERMISSION_USER_SET
+
+ assertThat(
+ permissionManager.getPermissionFlags(
+ TEST_PACKAGE_NAME,
+ deviceAwarePermission,
+ persistentDeviceId
+ )
+ )
+ .isEqualTo(0)
+
+ permissionManager.updatePermissionFlags(
+ TEST_PACKAGE_NAME,
+ deviceAwarePermission,
+ persistentDeviceId,
+ flagMask,
+ flag
+ )
+
+ assertThat(
+ permissionManager.getPermissionFlags(
+ TEST_PACKAGE_NAME,
+ deviceAwarePermission,
+ persistentDeviceId
+ )
+ )
+ .isEqualTo(FLAG_PERMISSION_USER_SET)
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testAllPermissionStatesApiGrantForVirtualDevice() {
+ // Setting a flag explicitly so that the permission consistently stays in the state
+ permissionManager.updatePermissionFlags(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED,
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ )
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)
+ .isEmpty()
+ )
+ .isTrue()
+
+ permissionManager.grantRuntimePermission(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ persistentDeviceId
+ )
+
+ val permissionStateMap =
+ permissionManager.getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)
+ assertThat(permissionStateMap.size).isEqualTo(1)
+ assertThat(permissionStateMap[DEVICE_AWARE_PERMISSION]!!.isGranted).isTrue()
+ assertThat(permissionStateMap[DEVICE_AWARE_PERMISSION]!!.flags).isEqualTo(0)
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)[
+ DEVICE_AWARE_PERMISSION]!!
+ .isGranted
+ )
+ .isFalse()
+
+ permissionManager.revokeRuntimePermission(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ persistentDeviceId,
+ "test"
+ )
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)
+ .contains(DEVICE_AWARE_PERMISSION)
+ )
+ .isFalse()
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun testAllPermissionStatesApiFlagsForVirtualDevice() {
+ val flagMask = FLAG_PERMISSION_USER_SET or FLAG_PERMISSION_USER_FIXED
+ val flag = FLAG_PERMISSION_USER_SET
+
+ assertThat(permissionManager.getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId))
+ .isEmpty()
+
+ permissionManager.updatePermissionFlags(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ persistentDeviceId,
+ flagMask,
+ flag
+ )
+
+ assertThat(
+ hasPermission(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)[
+ DEVICE_AWARE_PERMISSION]!!
+ .flags,
+ flag
+ )
+ )
+ .isTrue()
+
+ assertThat(
+ hasPermission(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)[
+ DEVICE_AWARE_PERMISSION]!!
+ .flags,
+ FLAG_PERMISSION_USER_FIXED
+ )
+ )
+ .isFalse()
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @Test
+ fun testAllPermissionStatesApiGrantForDefaultDevice() {
+ // Setting a flag explicitly so that the permission consistently stays in the state upon
+ // revoke
+ permissionManager.updatePermissionFlags(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED,
+ FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ )
+
+ permissionManager.grantRuntimePermission(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ PERSISTENT_DEVICE_ID_DEFAULT
+ )
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)[
+ DEVICE_AWARE_PERMISSION]!!
+ .isGranted
+ )
+ .isTrue()
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)
+ .contains(DEVICE_AWARE_PERMISSION)
+ )
+ .isFalse()
+
+ permissionManager.revokeRuntimePermission(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ "test"
+ )
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)[
+ DEVICE_AWARE_PERMISSION]!!
+ .isGranted
+ )
+ .isFalse()
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @Test
+ fun testAllPermissionStatesApiFlagsForDefaultDevice() {
+ val flagMask = FLAG_PERMISSION_USER_SET or FLAG_PERMISSION_USER_FIXED
+ val flag = FLAG_PERMISSION_USER_SET
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)
+ .contains(DEVICE_AWARE_PERMISSION)
+ )
+ .isFalse()
+
+ permissionManager.updatePermissionFlags(
+ TEST_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ PERSISTENT_DEVICE_ID_DEFAULT,
+ flagMask,
+ flag
+ )
+
+ assertThat(
+ hasPermission(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)[
+ DEVICE_AWARE_PERMISSION]!!
+ .flags,
+ flag
+ )
+ )
+ .isTrue()
+
+ assertThat(
+ hasPermission(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)[
+ DEVICE_AWARE_PERMISSION]!!
+ .flags,
+ FLAG_PERMISSION_USER_FIXED
+ )
+ )
+ .isFalse()
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @Test
+ fun testAllPermissionStatesApiThatNonDeviceAwareRuntimePermissionGrantIsNotInherited() {
+ permissionManager.grantRuntimePermission(
+ TEST_PACKAGE_NAME,
+ NON_DEVICE_AWARE_PERMISSION,
+ PERSISTENT_DEVICE_ID_DEFAULT
+ )
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, PERSISTENT_DEVICE_ID_DEFAULT)[
+ NON_DEVICE_AWARE_PERMISSION]!!
+ .isGranted
+ )
+ .isTrue()
+
+ assertThat(
+ permissionManager
+ .getAllPermissionStates(TEST_PACKAGE_NAME, persistentDeviceId)
+ .contains(NON_DEVICE_AWARE_PERMISSION)
+ )
+ .isFalse()
+ }
+
+ private fun hasPermission(permissionFlags: Int, permissionBit: Int): Boolean =
+ permissionFlags and permissionBit != 0
+
+ private fun revokeSelfPermission(permissionName: String, context: Context) {
+ val intent = Intent(PERMISSION_SELF_REVOKE_INTENT)
+ intent.setClassName(TEST_PACKAGE_NAME, PERMISSION_SELF_REVOKE_RECEIVER)
+ intent.putExtra("permissionName", permissionName)
+ intent.putExtra("deviceID", context.deviceId)
+ context.sendBroadcast(intent)
+ waitForBroadcasts()
+ }
+
+ private fun grantPermissionAndAssertGranted(permissionName: String, context: Context) {
+ context.packageManager.grantRuntimePermission(
+ TEST_PACKAGE_NAME,
+ permissionName,
+ UserHandle.of(context.userId)
+ )
+ assertPermission(permissionName, PERMISSION_GRANTED, context)
+ }
+
+ private fun revokePermissionAndAssertDenied(permissionName: String, context: Context) {
+ context.packageManager.revokeRuntimePermission(
+ TEST_PACKAGE_NAME,
+ permissionName,
+ UserHandle.of(context.userId)
+ )
+ assertPermission(permissionName, PERMISSION_DENIED, context)
+ }
+
+ private fun assertPermission(
+ permissionName: String,
+ permissionState: Int,
+ context: Context,
+ ) {
+ assertThat(context.packageManager.checkPermission(permissionName, TEST_PACKAGE_NAME))
+ .isEqualTo(permissionState)
+ }
+
+ companion object {
+ private const val TEST_PACKAGE_NAME = "android.permission.cts.appthatrequestpermission"
+ private const val TEST_APK =
+ "/data/local/tmp/cts-permission/CtsAppThatRequestsDevicePermissions.apk"
+
+ private const val CUSTOM_SIGNATURE_PERMISSION =
+ "android.permission.cts.CUSTOM_SIGNATURE_PERMISSION"
+
+ private const val PERMISSION_SELF_REVOKE_INTENT =
+ "android.permission.cts.appthatrequestpermission.REVOKE_SELF_PERMISSION"
+ private const val PERMISSION_SELF_REVOKE_RECEIVER =
+ "android.permission.cts.appthatrequestpermission.RevokeSelfPermissionReceiver"
+
+ private const val DEVICE_AWARE_PERMISSION = Manifest.permission.RECORD_AUDIO
+ private const val NON_DEVICE_AWARE_PERMISSION = Manifest.permission.READ_CONTACTS
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/DuplicatePermissionDefinitionsTest.kt b/tests/cts/permission/src/android/permission/cts/DuplicatePermissionDefinitionsTest.kt
new file mode 100644
index 000000000..ceb797b80
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/DuplicatePermissionDefinitionsTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts
+
+import android.content.pm.PackageManager
+import android.content.pm.PermissionGroupInfo
+import android.content.pm.PermissionInfo
+import android.os.Build
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SdkSuppress
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
+import com.android.compatibility.common.util.ShellUtils.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val APK_PATH = "/data/local/tmp/cts-permission/"
+
+private const val APK_DEFINING_PERM_A = "${APK_PATH}CtsAppThatDefinesPermissionA.apk"
+private const val APK_ALSO_DEFINING_PERM_A = "${APK_PATH}CtsAppThatAlsoDefinesPermissionA.apk"
+private const val APK_ALSO_DEFINING_PERM_A_DIFFERENT_CERT =
+ "${APK_PATH}CtsAppThatAlsoDefinesPermissionADifferentCert.apk"
+private const val APK_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT =
+ "${APK_PATH}CtsAppThatAlsoDefinesPermissionGroupADifferentCert.apk"
+private const val APK_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT_SDK_30 =
+ "${APK_PATH}CtsAppThatAlsoDefinesPermissionGroupADifferentCert30.apk"
+private const val APK_DEFINING_PERM_WITH_INVALID_GROUP =
+ "${APK_PATH}CtsAppThatDefinesPermissionWithInvalidGroup.apk"
+private const val APK_DEFINING_PERM_WITH_INVALID_GROUP_SDK_30 =
+ "${APK_PATH}CtsAppThatDefinesPermissionWithInvalidGroup30.apk"
+private const val APK_DEFINING_PERM_IN_PLATFORM_GROUP =
+ "${APK_PATH}CtsAppThatDefinesPermissionInPlatformGroup.apk"
+
+private const val APP_DEFINING_PERM_A = "android.permission.cts.appthatdefinespermissiona"
+private const val APP_ALSO_DEFINING_PERM_A = "android.permission.cts.appthatalsodefinespermissiona"
+private const val APP_ALSO_DEFINING_PERM_A_DIFFERENT_CERT =
+ "android.permission.cts.appthatdefinespermissiona.differentcert"
+private const val APP_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT =
+ "android.permission.cts.appthatdefinespermissiongroupa.differentcert"
+private const val APP_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT_SDK_30 =
+ "android.permission.cts.appthatdefinespermissiongroupa.differentcert30"
+private const val APP_DEFINING_PERM_IN_PLATFORM_GROUP =
+ "android.permission.cts.appthatdefinespermissioninplatformgroup"
+private const val APP_DEFINING_PERM_WITH_INVALID_GROUP =
+ "android.permission.cts.appthatdefinespermissionwithinvalidgroup"
+private const val APP_DEFINING_PERM_WITH_INVALID_GROUP_SDK_30 =
+ "android.permission.cts.appthatdefinespermissionwithinvalidgroup30"
+
+private const val PERM_A = "com.android.cts.duplicatepermission.permA"
+private const val GROUP_A = "com.android.cts.duplicatepermission.groupA"
+private const val INVALID_GROUP = "com.android.cts.duplicatepermission.invalid"
+
+/**
+ * Test cases where packages
+ * - define the same permission or
+ * - define the same permission group
+ * - define permissions in a group defined by another package
+ */
+@AppModeFull(reason = "Tests properties of other app. Instant apps cannot interact with other apps")
+@RunWith(AndroidJUnit4ClassRunner::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class DuplicatePermissionDefinitionsTest {
+ private val pm = InstrumentationRegistry.getTargetContext().packageManager
+
+ private fun install(apk: String) {
+ runShellCommandOrThrow("pm install $apk")
+ }
+
+ private fun uninstall(app: String) {
+ runShellCommand("pm uninstall $app")
+ }
+
+ private val allPackages: List<String>
+ get() = pm.getInstalledPackages(0).map { it.packageName }
+
+ private val permAInfo: PermissionInfo
+ get() = pm.getPermissionInfo(PERM_A, 0)!!
+
+ private val groupAInfo: PermissionGroupInfo
+ get() = pm.getPermissionGroupInfo(GROUP_A, 0)!!
+
+ @Test
+ fun canInstallAppsDefiningSamePermissionWhenSameCert() {
+ install(APK_DEFINING_PERM_A)
+ install(APK_ALSO_DEFINING_PERM_A)
+
+ assertThat(allPackages).containsAtLeast(APP_DEFINING_PERM_A, APP_ALSO_DEFINING_PERM_A)
+
+ assertThat(permAInfo.packageName).isEqualTo(APP_DEFINING_PERM_A)
+ }
+
+ @Test
+ fun cannotInstallAppsDefiningSamePermissionWhenDifferentCert() {
+ install(APK_DEFINING_PERM_A)
+ install(APK_ALSO_DEFINING_PERM_A_DIFFERENT_CERT)
+
+ assertThat(allPackages).contains(APP_DEFINING_PERM_A)
+ assertThat(allPackages).doesNotContain(APP_ALSO_DEFINING_PERM_A_DIFFERENT_CERT)
+
+ assertThat(permAInfo.packageName).isEqualTo(APP_DEFINING_PERM_A)
+ }
+
+ @Test
+ fun canInstallAppsDefiningSamePermissionGroupWhenDifferentCertIfSdk30() {
+ install(APK_DEFINING_PERM_A)
+ install(APK_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT_SDK_30)
+
+ assertThat(allPackages)
+ .containsAtLeast(
+ APP_DEFINING_PERM_A,
+ APP_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT_SDK_30
+ )
+
+ assertThat(groupAInfo.packageName).isEqualTo(APP_DEFINING_PERM_A)
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = [146211400])
+ fun cannotInstallAppsDefiningSamePermissionGroupWhenDifferentCert() {
+ install(APK_DEFINING_PERM_A)
+ install(APK_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT)
+
+ assertThat(allPackages).contains(APP_DEFINING_PERM_A)
+ assertThat(allPackages).doesNotContain(APP_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT)
+
+ assertThat(groupAInfo.packageName).isEqualTo(APP_DEFINING_PERM_A)
+ }
+
+ // This is the same as cannotInstallAppsDefiningSamePermissionGroupWhenDifferentCert but this
+ // case is allowed as the package that originally defined the group is a platform.
+ @Test
+ fun canInstallAppsDefiningPermissionInPlatformGroup() {
+ install(APK_DEFINING_PERM_IN_PLATFORM_GROUP)
+
+ assertThat(allPackages).contains(APP_DEFINING_PERM_IN_PLATFORM_GROUP)
+
+ assertThat(permAInfo.packageName).isEqualTo(APP_DEFINING_PERM_IN_PLATFORM_GROUP)
+ assertThat(permAInfo.group).isEqualTo(android.Manifest.permission_group.CAMERA)
+ assertThat(
+ pm.getPermissionGroupInfo(android.Manifest.permission_group.CAMERA, 0)!!.packageName
+ )
+ .isEqualTo("android")
+ }
+
+ @Test
+ fun canInstallAppsDefiningPermissionWithInvalidGroupSdk30() {
+ install(APK_DEFINING_PERM_WITH_INVALID_GROUP_SDK_30)
+
+ assertThat(allPackages).contains(APP_DEFINING_PERM_WITH_INVALID_GROUP_SDK_30)
+
+ assertThat(permAInfo.packageName).isEqualTo(APP_DEFINING_PERM_WITH_INVALID_GROUP_SDK_30)
+ assertThat(permAInfo.group).isEqualTo(INVALID_GROUP)
+ }
+
+ @Test(expected = PackageManager.NameNotFoundException::class)
+ @AsbSecurityTest(cveBugId = [146211400])
+ fun cannotInstallAppsDefiningPermissionWithInvalidGroup() {
+ install(APK_DEFINING_PERM_WITH_INVALID_GROUP)
+
+ assertThat(allPackages).doesNotContain(APP_DEFINING_PERM_WITH_INVALID_GROUP)
+
+ // throws a NameNotFoundException as perm info does not exist
+ permAInfo
+ }
+
+ @After
+ fun uninstallTestApps() {
+ uninstall(APP_DEFINING_PERM_A)
+ uninstall(APP_ALSO_DEFINING_PERM_A)
+ uninstall(APP_ALSO_DEFINING_PERM_A_DIFFERENT_CERT)
+ uninstall(APP_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT)
+ uninstall(APP_ALSO_DEFINING_PERM_GROUP_A_DIFFERENT_CERT_SDK_30)
+ uninstall(APP_DEFINING_PERM_IN_PLATFORM_GROUP)
+ uninstall(APP_DEFINING_PERM_WITH_INVALID_GROUP)
+ uninstall(APP_DEFINING_PERM_WITH_INVALID_GROUP_SDK_30)
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/EthernetManagerPermissionTest.java b/tests/cts/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
new file mode 100644
index 000000000..3c99b34a9
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/EthernetManagerPermissionTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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 android.permission.cts;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.EthernetManager;
+import android.net.EthernetNetworkUpdateRequest;
+import android.net.IpConfiguration;
+import android.net.NetworkCapabilities;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+
+/**
+ * Test protected android.net.EthernetManager methods cannot be called without permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class EthernetManagerPermissionTest {
+ private static final String TEST_IFACE = "test123abc789";
+ private EthernetManager mEthernetManager;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mEthernetManager = mContext.getSystemService(EthernetManager.class);
+ // mEthernetManager may be null depending on the device's configuration.
+ assumeNotNull(mEthernetManager);
+ }
+
+ private EthernetNetworkUpdateRequest buildUpdateRequest() {
+ return new EthernetNetworkUpdateRequest.Builder()
+ .setIpConfiguration(new IpConfiguration.Builder().build())
+ .setNetworkCapabilities(new NetworkCapabilities.Builder().build())
+ .build();
+ }
+
+ private EthernetNetworkUpdateRequest buildUpdateRequestWithoutCapabilities() {
+ return new EthernetNetworkUpdateRequest.Builder()
+ .setIpConfiguration(new IpConfiguration.Builder().build())
+ .build();
+ }
+
+ /**
+ * Verify that calling {@link EthernetManager#updateConfiguration(String,
+ * EthernetNetworkUpdateRequest, Executor, BiConsumer)} requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#MANAGE_ETHERNET_NETWORKS}.
+ */
+ @Test
+ public void testUpdateConfigurationRequiresPermissionManageEthernetNetworks() {
+ assertThrows("Should not be able to call updateConfiguration without permission",
+ SecurityException.class,
+ () -> mEthernetManager.updateConfiguration(TEST_IFACE,
+ buildUpdateRequestWithoutCapabilities(), null, null));
+ }
+
+ /**
+ * Verify that calling {@link EthernetManager#enableInterface}
+ * requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#MANAGE_ETHERNET_NETWORKS}.
+ */
+ @Test
+ public void testEnableInterface() {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE));
+ assertThrows("Should not be able to call enableInterface without permission",
+ SecurityException.class,
+ () -> mEthernetManager.enableInterface(TEST_IFACE, null, null));
+ }
+
+ /**
+ * Verify that calling {@link EthernetManager#disableInterface}
+ * requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#MANAGE_ETHERNET_NETWORKS}.
+ */
+ @Test
+ public void testDisableInterface() {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE));
+ assertThrows("Should not be able to call disableInterface without permission",
+ SecurityException.class,
+ () -> mEthernetManager.disableInterface(TEST_IFACE, null, null));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java
new file mode 100644
index 000000000..94557464f
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -0,0 +1,1279 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStatVfs;
+import android.util.Pair;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.PropertyUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Verify certain permissions on the filesystem
+ *
+ * TODO: Combine this file with {@link android.os.cts.FileAccessPermissionTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class FileSystemPermissionTest {
+
+ private int dumpable;
+
+ @Before
+ public void setUp() throws Exception {
+ dumpable = Os.prctl(OsConstants.PR_GET_DUMPABLE, 0, 0, 0, 0);
+ Os.prctl(OsConstants.PR_SET_DUMPABLE, 1, 0, 0, 0);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Os.prctl(OsConstants.PR_SET_DUMPABLE, dumpable, 0, 0, 0);
+ }
+
+ @MediumTest
+ @Test
+ public void testCreateFileHasSanePermissions() throws Exception {
+ File myFile = new File(getContext().getFilesDir(), "hello");
+ FileOutputStream stream = new FileOutputStream(myFile);
+ stream.write("hello world".getBytes());
+ stream.close();
+ try {
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ FileUtils.getFileStatus(myFile.getAbsolutePath(), status, false);
+ int expectedPerms = FileUtils.S_IFREG
+ | FileUtils.S_IWUSR
+ | FileUtils.S_IRUSR;
+ assertEquals(
+ "Newly created files should have 0600 permissions",
+ Integer.toOctalString(expectedPerms),
+ Integer.toOctalString(status.mode));
+ } finally {
+ assertTrue(myFile.delete());
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testCreateDirectoryHasSanePermissions() throws Exception {
+ File myDir = new File(getContext().getFilesDir(), "helloDirectory");
+ assertTrue(myDir.mkdir());
+ try {
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ FileUtils.getFileStatus(myDir.getAbsolutePath(), status, false);
+ int expectedPerms = FileUtils.S_IFDIR
+ | FileUtils.S_IWUSR
+ | FileUtils.S_IRUSR
+ | FileUtils.S_IXUSR;
+ assertEquals(
+ "Newly created directories should have 0700 permissions",
+ Integer.toOctalString(expectedPerms),
+ Integer.toOctalString(status.mode));
+
+ } finally {
+ assertTrue(myDir.delete());
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testOtherApplicationDirectoriesAreNotWritable() throws Exception {
+ Set<File> writableDirs = new HashSet<File>();
+ List<ApplicationInfo> apps = getContext()
+ .getPackageManager()
+ .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
+ String myAppDirectory = getContext().getApplicationInfo().dataDir;
+ for (ApplicationInfo app : apps) {
+ if (app.dataDir != null && !myAppDirectory.equals(app.dataDir)) {
+ writableDirs.addAll(getWritableDirectoriesAndSubdirectoriesOf(new File(app.dataDir)));
+ }
+ }
+
+ assertTrue("Found writable directories: " + writableDirs.toString(),
+ writableDirs.isEmpty());
+ }
+
+ @MediumTest
+ @Test
+ public void testApplicationParentDirectoryNotWritable() throws Exception {
+ String myDataDir = getContext().getApplicationInfo().dataDir;
+ File parentDir = new File(myDataDir).getParentFile();
+ assertFalse(parentDir.toString(), isDirectoryWritable(parentDir));
+ }
+
+ @MediumTest
+ @Test
+ public void testDataDirectoryNotWritable() throws Exception {
+ assertFalse(isDirectoryWritable(Environment.getDataDirectory()));
+ }
+
+ @MediumTest
+ @Test
+ public void testAndroidRootDirectoryNotWritable() throws Exception {
+ assertFalse(isDirectoryWritable(Environment.getRootDirectory()));
+ }
+
+ @MediumTest
+ @Test
+ public void testDownloadCacheDirectoryNotWritable() throws Exception {
+ assertFalse(isDirectoryWritable(Environment.getDownloadCacheDirectory()));
+ }
+
+ @MediumTest
+ @Test
+ public void testRootDirectoryNotWritable() throws Exception {
+ assertFalse(isDirectoryWritable(new File("/")));
+ }
+
+ @MediumTest
+ @Test
+ public void testDevDirectoryNotWritable() throws Exception {
+ assertFalse(isDirectoryWritable(new File("/dev")));
+ }
+
+ @MediumTest
+ @Test
+ public void testProcDirectoryNotWritable() throws Exception {
+ assertFalse(isDirectoryWritable(new File("/proc")));
+ }
+
+ @MediumTest
+ @Test
+ public void testDevDiagSane() throws Exception {
+ File f = new File("/dev/diag");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ /* b/26813932 */
+ @MediumTest
+ @Test
+ public void testProcInterruptsNotReadable() throws Exception {
+ File f = new File("/proc/interrupts");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ /* b/26813932 */
+ @MediumTest
+ @Test
+ public void testProcStatNotReadable() throws Exception {
+ File f = new File("/proc/stat");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ @MediumTest
+ @Test
+ public void testDevMemSane() throws Exception {
+ File f = new File("/dev/mem");
+ assertFalse(f.exists());
+ }
+
+ @MediumTest
+ @Test
+ public void testDevkmemSane() throws Exception {
+ File f = new File("/dev/kmem");
+ assertFalse(f.exists());
+ }
+
+ @MediumTest
+ @Test
+ public void testDevPortSane() throws Exception {
+ File f = new File("/dev/port");
+ assertFalse(f.exists());
+ }
+
+ @MediumTest
+ @Test
+ public void testPn544Sane() throws Exception {
+ File f = new File("/dev/pn544");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "nfc");
+ assertFileOwnedByGroup(f, "nfc");
+ }
+
+ @MediumTest
+ @Test
+ public void testBcm2079xSane() throws Exception {
+ File f = new File("/dev/bcm2079x");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "nfc");
+ assertFileOwnedByGroup(f, "nfc");
+ }
+
+ @MediumTest
+ @Test
+ public void testBcm2079xi2cSane() throws Exception {
+ File f = new File("/dev/bcm2079x-i2c");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "nfc");
+ assertFileOwnedByGroup(f, "nfc");
+ }
+
+ @MediumTest
+ @Test
+ public void testDevQtaguidSane() throws Exception {
+ File f = new File("/dev/xt_qtaguid");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "root");
+ }
+
+ @MediumTest
+ @Test
+ public void testProcQtaguidCtrlSane() throws Exception {
+ File f = new File("/proc/net/xt_qtaguid/ctrl");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "net_bw_acct");
+ }
+
+ @MediumTest
+ @Test
+ public void testProcQtaguidStatsSane() throws Exception {
+ File f = new File("/proc/net/xt_qtaguid/stats");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "net_bw_stats");
+ }
+
+ private static List<String> procNetFiles = Arrays.asList("anycast6", "arp", "arp_tables_matches",
+ "arp_tables_names", "arp_tables_targets", "dev", "dev_mcast", "fib_trie", "fib_triestat",
+ "hci", "icmp", "icmp6", "if_inet6", "igmp", "igmp6", "ip6_flowlabel",
+ "ip6_tables_matches", "ip6_tables_names", "ip6_tables_targets", "ip_tables_matches",
+ "ip_tables_names", "ip_tables_targets", "ipv6_route", "l2cap", "mcfilter", "mcfilter6",
+ "netlink", "netstat", "nf_conntrack", "nf_conntrack_expect", "packet", "pfkey", "pnp",
+ "pppoe", "pppol2tp", "protocols", "psched", "ptype", "raw", "raw6", "route", "rt6_stats",
+ "rt_cache", "sco", "snmp", "snmp6", "sockstat", "sockstat6", "softnet_stat", "tcp",
+ "tcp6", "udp", "udp6", "udplite", "udplite6", "unix", "wireless", "xfrm_stat");
+
+ private static void procNetSane(String path) {
+ File f = new File(path);
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "root");
+ }
+
+ @MediumTest
+ @Test
+ public void testProcNetSane() throws Exception {
+ if (PropertyUtil.isVendorApiLevelNewerThan(28)) {
+ for (String file : procNetFiles) {
+ procNetSane("/proc/net/" + file);
+ }
+ }
+ }
+
+ private static int readInt(File f) throws FileNotFoundException {
+ try (Scanner s = new Scanner(f)) {
+ return s.nextInt();
+ }
+ }
+
+ private static boolean writeInt(File f, int value) throws IOException {
+ try (FileOutputStream os = new FileOutputStream(f)) {
+ try {
+ os.write(Integer.toString(value).getBytes());
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testProcSelfOomAdjSane() throws IOException {
+ final int OOM_DISABLE = -17;
+
+ File f = new File("/proc/self/oom_adj");
+ assertTrue(f.canRead());
+ assertFalse(f.canExecute());
+
+ int oom_adj = readInt(f);
+ assertNotEquals("unprivileged processes should not be unkillable", OOM_DISABLE, oom_adj);
+ if (f.canWrite())
+ assertFalse("unprivileged processes should not be able to reduce their oom_adj value",
+ writeInt(f, oom_adj - 1));
+ }
+
+ @MediumTest
+ @Test
+ public void testProcSelfOomScoreAdjSane() throws IOException {
+ final int OOM_SCORE_ADJ_MIN = -1000;
+
+ File f = new File("/proc/self/oom_score_adj");
+ assertTrue(f.canRead());
+ assertFalse(f.canExecute());
+
+ int oom_score_adj = readInt(f);
+ assertNotEquals("unprivileged processes should not be unkillable", OOM_SCORE_ADJ_MIN, oom_score_adj);
+ if (f.canWrite()) {
+ assertFalse(
+ "unprivileged processes should not be able to reduce their oom_score_adj value",
+ writeInt(f, oom_score_adj - 1));
+ assertTrue(
+ "unprivileged processes should be able to increase their oom_score_adj value",
+ writeInt(f, oom_score_adj + 1));
+ assertTrue("unprivileged processes should be able to restore their oom_score_adj value",
+ writeInt(f, oom_score_adj));
+ }
+ }
+
+ private static List<Pair<Long, Long>> mappedPageRanges() throws IOException {
+ final BigInteger PAGE_SIZE = new BigInteger("4096");
+
+ final Pattern mapsPattern = Pattern.compile("^(\\p{XDigit}+)-(\\p{XDigit}+)");
+ List<Pair<Long, Long>> ret = new LinkedList<>();
+
+ BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"));
+ String line;
+ try {
+ while ((line = reader.readLine()) != null) {
+ Matcher m = mapsPattern.matcher(line);
+ m.find();
+
+ long start = new BigInteger(m.group(1), 16).divide(PAGE_SIZE).longValue();
+ long end = new BigInteger(m.group(2), 16).divide(PAGE_SIZE).longValue();
+
+ ret.add(new Pair<>(start, end));
+ }
+
+ return ret;
+ } finally {
+ reader.close();
+ }
+ }
+
+ private static boolean pfnIsZero(FileDescriptor pagemap, long start, long end) throws ErrnoException, IOException {
+ // Note: reads from /proc/self/pagemap *must* be 64-bit aligned. Use low-level android.system.Os routines to
+ // ensure this.
+ final int SIZEOF_U64 = 8;
+ final long PAGE_PRESENT = 1L << 63;
+ final long PFN_MASK = (1L << 55) - 1;
+
+ for (long page = start; page < end; page++) {
+ long offset = page * SIZEOF_U64;
+ long seek = Os.lseek(pagemap, offset, OsConstants.SEEK_SET);
+ if (offset != seek)
+ throw new IOException("lseek(" + offset + ") returned " + seek);
+
+ byte bytes[] = new byte[SIZEOF_U64];
+ ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.nativeOrder());
+ int read = Os.read(pagemap, buf);
+
+ if (read == 0)
+ // /proc/[pid]/maps may contain entries that are outside the process's VM space,
+ // like the [vectors] page on 32-bit ARM devices. In this case, seek() succeeds but
+ // read() returns 0. The kernel is telling us that there are no more pagemap
+ // entries to read, so we can stop here.
+ break;
+ else if (read != bytes.length)
+ throw new IOException("read(" + bytes.length + ") returned " + read);
+
+ buf.position(0);
+ long entry = buf.getLong();
+ if ((entry & PAGE_PRESENT) == PAGE_PRESENT && (entry & PFN_MASK) != 0)
+ return false;
+ }
+
+ return true;
+ }
+
+ @MediumTest
+ @Test
+ public void testProcSelfPagemapSane() throws ErrnoException, IOException {
+ FileDescriptor pagemap = null;
+ try {
+ pagemap = Os.open("/proc/self/pagemap", OsConstants.O_RDONLY, 0);
+
+ for (Pair<Long, Long> range : mappedPageRanges())
+ if (!pfnIsZero(pagemap, range.first, range.second))
+ fail("Device is missing the following kernel security patch: "
+ + "https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ab676b7d6fbf4b294bf198fb27ade5b0e865c7ce");
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EPERM)
+ // expected before 4.2
+ return;
+
+ throw e;
+ } finally {
+ if (pagemap != null)
+ Os.close(pagemap);
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testTcpDefaultRwndSane() throws Exception {
+ File f = new File("/proc/sys/net/ipv4/tcp_default_init_rwnd");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "root");
+ }
+
+ @MediumTest
+ @Test
+ public void testIdletimerDirectoryExistsAndSane() throws Exception {
+ File dir = new File("/sys/class/xt_idletimer");
+ assertTrue(dir.isDirectory());
+ assertFalse(dir.canWrite());
+ assertTrue(dir.canExecute());
+
+ assertFileOwnedBy(dir, "root");
+ assertFileOwnedByGroup(dir, "root");
+ }
+
+
+ @MediumTest
+ @Test
+ public void testProcfsMmapRndBitsExistsAndSane() throws Exception {
+ String arch = System.getProperty("os.arch");
+ boolean supported = false;
+ boolean supported_64 = false;
+
+ if (arch.equals("aarch64") || arch.equals("x86_64"))
+ supported_64 = true;
+ else if (arch.startsWith("arm") || arch.endsWith("86"))
+ supported = true;
+
+ /* 64-bit OS should support running 32-bit applications */
+ if (supported_64) {
+ File f = new File("/proc/sys/vm/mmap_rnd_compat_bits");
+ assertTrue(f.exists());
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ if (supported_64 || supported) {
+ File f = new File("/proc/sys/vm/mmap_rnd_bits");
+ assertTrue(f.exists());
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+ }
+
+ /**
+ * Assert that a file is owned by a specific owner. This is a noop if the
+ * file does not exist.
+ *
+ * @param file The file to check.
+ * @param expectedOwner The owner of the file.
+ */
+ private static void assertFileOwnedBy(File file, String expectedOwner) {
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ String path = file.getAbsolutePath();
+ if (file.exists() && FileUtils.getFileStatus(path, status, true)) {
+ String actualOwner = FileUtils.getUserName(status.uid);
+ if (!expectedOwner.equals(actualOwner)) {
+ String msg = String.format("Wrong owner. Expected '%s', but found '%s' for %s.",
+ expectedOwner, actualOwner, path);
+ fail(msg);
+ }
+ }
+ }
+
+ /**
+ * Assert that a file is owned by a specific group. This is a noop if the
+ * file does not exist.
+ *
+ * @param file The file to check.
+ * @param expectedGroup The owner group of the file.
+ */
+ private static void assertFileOwnedByGroup(File file, String expectedGroup) {
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ String path = file.getAbsolutePath();
+ if (file.exists() && FileUtils.getFileStatus(path, status, true)) {
+ String actualGroup = FileUtils.getGroupName(status.gid);
+ if (!expectedGroup.equals(actualGroup)) {
+ String msg = String.format("Wrong group. Expected '%s', but found '%s' for %s.",
+ expectedGroup, actualGroup, path);
+ fail(msg);
+ }
+ }
+ }
+
+ @MediumTest
+ @Test
+ public void testTtyO3Sane() throws Exception {
+ File f = new File("/dev/ttyO3");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ @MediumTest
+ @Test
+ public void testDataMediaSane() throws Exception {
+ final File f = new File("/data/media");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ @MediumTest
+ @Test
+ public void testMntShellSane() throws Exception {
+ final File f = new File("/mnt/shell");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ @MediumTest
+ @Test
+ public void testMntSecureSane() throws Exception {
+ final File f = new File("/mnt/secure");
+ assertFalse(f.canRead());
+ assertFalse(f.canWrite());
+ assertFalse(f.canExecute());
+ }
+
+ private static boolean isDirectoryWritable(File directory) {
+ File toCreate = new File(directory, "hello");
+ try {
+ toCreate.createNewFile();
+ return true;
+ } catch (IOException e) {
+ // It's expected we'll get a "Permission denied" exception.
+ } finally {
+ toCreate.delete();
+ }
+ return false;
+ }
+
+ /**
+ * Verify that any publicly readable directories reachable from
+ * the root directory are not writable. An application should only be
+ * able to write to it's own home directory. World writable directories
+ * are a security hole because they enable a number of different attacks.
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/Symlink_race">Symlink Races</a></li>
+ * <li>Data destruction by deleting or renaming files you don't own</li>
+ * <li>Data substitution by replacing trusted files with untrusted files</li>
+ * </ul>
+ *
+ * Note: Because not all directories are readable, this is a best-effort
+ * test only. Writable directories within unreadable subdirectories
+ * will NOT be detected by this code.
+ */
+ @LargeTest
+ @Test
+ public void testAllOtherDirectoriesNotWritable() throws Exception {
+ File start = new File("/");
+ Set<File> writableDirs = getWritableDirectoriesAndSubdirectoriesOf(start);
+
+ assertTrue("Found writable directories: " + writableDirs.toString(),
+ writableDirs.isEmpty());
+ }
+
+ private static final Set<String> OTHER_RANDOM_DIRECTORIES = new HashSet<String>(
+ Arrays.asList(
+ "/app-cache",
+ "/app-cache/ciq/socket",
+ "/cache/fotapkg",
+ "/cache/fotapkg/tmp",
+ "/data/_SamsungBnR_",
+ "/data/_SamsungBnR_/BR",
+ "/data/2nd-init",
+ "/data/amit",
+ "/data/anr",
+ "/data/app",
+ "/data/app-private",
+ "/data/backup",
+ "/data/battd",
+ "/data/bootlogo",
+ "/data/btips",
+ "/data/btips/TI",
+ "/data/btips/TI/opp",
+ "/data/cache",
+ "/data/calibration",
+ "/data/clipboard",
+ "/data/clp",
+ "/data/dalvik-cache",
+ "/data/data",
+ "/data/data/.drm",
+ "/data/data/.drm/.wmdrm",
+ "/data/data/cw",
+ "/data/data/com.android.htcprofile",
+ "/data/data/com.android.providers.drm/rights",
+ "/data/data/com.htc.android.qxdm2sd",
+ "/data/data/com.htc.android.qxdm2sd/bin",
+ "/data/data/com.htc.android.qxdm2sd/data",
+ "/data/data/com.htc.android.qxdm2sd/tmp",
+ "/data/data/com.htc.android.netlogger/data",
+ "/data/data/com.htc.messagecs/att",
+ "/data/data/com.htc.messagecs/pdu",
+ "/data/data/com.htc.loggers/bin",
+ "/data/data/com.htc.loggers/data",
+ "/data/data/com.htc.loggers/htclog",
+ "/data/data/com.htc.loggers/tmp",
+ "/data/data/com.htc.loggers/htcghost",
+ "/data/data/com.lge.ers/android",
+ "/data/data/com.lge.ers/arm9",
+ "/data/data/com.lge.ers/kernel",
+ "/data/data/com.lge.wmc",
+ "/data/data/com.redbend.vdmc/lib",
+ "/data/data/recovery",
+ "/data/data/recovery/HTCFOTA",
+ "/data/data/recovery/OMADM",
+ "/data/data/shared",
+ "/data/diag_logs",
+ "/data/dontpanic",
+ "/data/drm",
+ "/data/drm/fwdlock",
+ "/data/drm/IDM",
+ "/data/drm/IDM/HTTP",
+ "/data/drm/rights",
+ "/data/dump",
+ "/data/efslog",
+ "/data/emt",
+ "/data/factory",
+ "/data/fics",
+ "/data/fics/dev",
+ "/data/fota",
+ "/data/gps",
+ "/data/gps/log",
+ "/data/gps/var",
+ "/data/gps/var/run",
+ "/data/gpscfg",
+ "/data/hwvefs",
+ "/data/htcfs",
+ "/data/img",
+ "/data/install",
+ "/data/internal-device",
+ "/data/internal-device/DCIM",
+ "/data/last_alog",
+ "/data/last_klog",
+ "/data/local",
+ "/data/local/logs",
+ "/data/local/logs/kernel",
+ "/data/local/logs/logcat",
+ "/data/local/logs/resetlog",
+ "/data/local/logs/smem",
+ "/data/local/mono",
+ "/data/local/mono/pulse",
+ "/data/local/purple",
+ "/data/local/purple/sound",
+ "/data/local/rights",
+ "/data/local/rwsystag",
+ "/data/local/skel",
+ "/data/local/skel/default",
+ "/data/local/skel/defualt", // Mispelled "defualt" is intentional
+ "/data/local/tmp",
+ "/data/local/tmp/com.nuance.android.vsuite.vsuiteapp",
+ "/data/log",
+ "/data/logger",
+ "/data/logs",
+ "/data/logs/core",
+ "/data/lost+found",
+ "/data/mdl",
+ "/data/misc",
+ "/data/misc/bluetooth",
+ "/data/misc/bluetooth/logs",
+ "/data/misc/dhcp",
+ "/data/misc/lockscreen",
+ "/data/misc/sensor",
+ "/data/misc/webwidgets",
+ "/data/misc/webwidgets/chess",
+ "/data/misc/widgets",
+ "/data/misc/wifi",
+ "/data/misc/wifi/sockets",
+ "/data/misc/wimax",
+ "/data/misc/wimax/sockets",
+ "/data/misc/wminput",
+ "/data/misc/wpa_supplicant",
+ "/data/nv",
+ "/data/nvcam",
+ "/data/panic",
+ "/data/panicreports",
+ "/data/preinstall_md5",
+ "/data/property",
+ "/data/radio",
+ "/data/secure",
+ "/data/security",
+ "/data/sensors",
+ "/data/shared",
+ "/data/simcom",
+ "/data/simcom/btadd",
+ "/data/simcom/simlog",
+ "/data/system",
+ "/data/tmp",
+ "/data/tombstones",
+ "/data/tombstones/ramdump",
+ "/data/tpapi",
+ "/data/tpapi/etc",
+ "/data/tpapi/etc/tpa",
+ "/data/tpapi/etc/tpa/persistent",
+ "/data/tpapi/user.bin",
+ "/data/vpnch",
+ "/data/wapi",
+ "/data/wifi",
+ "/data/wimax",
+ "/data/wimax/log",
+ "/data/wiper",
+ "/data/wpstiles",
+ "/data/xt9",
+ "/dbdata/databases",
+ "/efs/.android",
+ "/mnt/sdcard",
+ "/mnt/usbdrive",
+ "/mnt_ext",
+ "/mnt_ext/badablk2",
+ "/mnt_ext/badablk3",
+ "/mnt_ext/cache",
+ "/mnt_ext/data",
+ "/system/etc/security/drm",
+ "/synthesis/hades",
+ "/synthesis/chimaira",
+ "/synthesis/shdisp",
+ "/synthesis/hdmi",
+ "/tmp"
+ )
+ );
+
+ /**
+ * Verify that directories not discoverable by
+ * testAllOtherDirectoriesNotWritable are not writable. An application
+ * should only be able to write to it's own home directory. World
+ * writable directories are a security hole because they enable a
+ * number of different attacks.
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/Symlink_race">Symlink Races</a></li>
+ * <li>Data destruction by deleting or renaming files you don't own</li>
+ * <li>Data substitution by replacing trusted files with untrusted files</li>
+ * </ul>
+ *
+ * Because /data and /data/data are not readable, we blindly try to
+ * poke around in there looking for bad directories. There has to be
+ * a better way...
+ */
+ @LargeTest
+ @Test
+ public void testOtherRandomDirectoriesNotWritable() throws Exception {
+ Set<File> writableDirs = new HashSet<File>();
+ for (String dir : OTHER_RANDOM_DIRECTORIES) {
+ File start = new File(dir);
+ writableDirs.addAll(getWritableDirectoriesAndSubdirectoriesOf(start));
+ }
+
+ assertTrue("Found writable directories: " + writableDirs.toString(),
+ writableDirs.isEmpty());
+ }
+
+ @LargeTest
+ @Test
+ public void testReadingSysFilesDoesntFail() throws Exception {
+ ExecutorService executor = Executors.newCachedThreadPool();
+ tryToReadFromAllIn(new File("/sys"), executor);
+ executor.shutdownNow();
+ }
+
+ private static void tryToReadFromAllIn(File dir, ExecutorService executor) throws IOException {
+ assertTrue(dir.isDirectory());
+
+ if (isSymbolicLink(dir)) {
+ // don't examine symbolic links.
+ return;
+ }
+
+ File[] files = dir.listFiles();
+
+ if (files != null) {
+ for (File f : files) {
+ if (f.isDirectory()) {
+ tryToReadFromAllIn(f, executor);
+ } else {
+ tryFileOpenRead(f, executor);
+ }
+ }
+ }
+ }
+
+ private static void tryFileOpenRead(final File f, ExecutorService executor) throws IOException {
+ // Callable requires stack variables to be final.
+ Callable<Boolean> readFile = new Callable<Boolean>() {
+ @Override
+ public Boolean call() throws Exception {
+ return tryFileRead(f);
+ }
+ };
+
+ Boolean completed = false;
+ String fileName = null;
+ Future<Boolean> future = null;
+ try {
+ fileName = f.getCanonicalPath();
+
+ future = executor.submit(readFile);
+
+ // Block, waiting no more than set seconds.
+ completed = future.get(3, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ System.out.println("TIMEOUT: " + fileName);
+ } catch (InterruptedException e) {
+ System.out.println("INTERRUPTED: " + fileName);
+ } catch (ExecutionException e) {
+ System.out.println("TASK WAS ABORTED BY EXCEPTION: " + fileName);
+ } catch (IOException e) {
+ // File.getCanonicalPath() will throw this.
+ } finally {
+ if (future != null) {
+ future.cancel(true);
+ }
+ }
+ }
+
+ private static Boolean tryFileRead(File f) {
+ byte[] b = new byte[1024];
+ try {
+ System.out.println("looking at " + f.getCanonicalPath());
+
+ FileInputStream fis = new FileInputStream(f);
+ while((fis.available() != 0) && (fis.read(b) != -1)) {
+ // throw away data
+ }
+
+ fis.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ return true;
+ }
+
+ private static final Set<File> SYS_EXCEPTIONS = new HashSet<File>(
+ Arrays.asList(
+ new File("/sys/kernel/debug/tracing/trace_marker"),
+ new File("/sys/fs/selinux/member"),
+ new File("/sys/fs/selinux/user"),
+ new File("/sys/fs/selinux/relabel"),
+ new File("/sys/fs/selinux/create"),
+ new File("/sys/fs/selinux/access"),
+ new File("/sys/fs/selinux/context"),
+ new File("/sys/fs/selinux/validatetrans")
+ ));
+
+ @LargeTest
+ @Test
+ public void testAllFilesInSysAreNotWritable() throws Exception {
+ Set<File> writable = getAllWritableFilesInDirAndSubDir(new File("/sys"));
+ writable.removeAll(SYS_EXCEPTIONS);
+ assertTrue("Found writable: " + writable.toString(),
+ writable.isEmpty());
+ }
+
+ private static Set<File>
+ getAllWritableFilesInDirAndSubDir(File dir) throws Exception {
+ assertTrue(dir.isDirectory());
+ Set<File> retval = new HashSet<File>();
+
+ if (isSymbolicLink(dir)) {
+ // don't examine symbolic links.
+ return retval;
+ }
+
+ File[] subDirectories = dir.listFiles(new FileFilter() {
+ @Override public boolean accept(File pathname) {
+ return pathname.isDirectory();
+ }
+ });
+
+
+ /* recurse into subdirectories */
+ if (subDirectories != null) {
+ for (File f : subDirectories) {
+ retval.addAll(getAllWritableFilesInDirAndSubDir(f));
+ }
+ }
+
+ File[] filesInThisDirectory = dir.listFiles(new FileFilter() {
+ @Override public boolean accept(File pathname) {
+ return pathname.isFile();
+ }
+ });
+ if (filesInThisDirectory == null) {
+ return retval;
+ }
+
+ for (File f: filesInThisDirectory) {
+ if (f.canWrite()) {
+ retval.add(f.getCanonicalFile());
+ }
+ }
+ return retval;
+ }
+
+ @Test
+ public void testSystemMountedRO() throws Exception {
+ StructStatVfs vfs = Os.statvfs("/system");
+ assertTrue("/system is not mounted read-only", (vfs.f_flag & OsConstants.ST_RDONLY) != 0);
+ }
+
+ @Test
+ public void testRootMountedRO() throws Exception {
+ StructStatVfs vfs = Os.statvfs("/");
+ assertTrue("rootfs is not mounted read-only", (vfs.f_flag & OsConstants.ST_RDONLY) != 0);
+ }
+
+ @Test
+ public void testVendorMountedRO() throws Exception {
+ StructStatVfs vfs = Os.statvfs("/vendor");
+ assertTrue("/vendor is not mounted read-only", (vfs.f_flag & OsConstants.ST_RDONLY) != 0);
+ }
+
+ @Test
+ public void testOdmMountedRO() throws Exception {
+ StructStatVfs vfs = Os.statvfs("/odm");
+ assertTrue("/odm is not mounted read-only", (vfs.f_flag & OsConstants.ST_RDONLY) != 0);
+ }
+
+ @Test
+ public void testOemMountedRO() throws Exception {
+ StructStatVfs vfs = Os.statvfs("/oem");
+ assertTrue("/oem is not mounted read-only", (vfs.f_flag & OsConstants.ST_RDONLY) != 0);
+ }
+
+ @Test
+ public void testDataMountedNoSuidNoDev() throws Exception {
+ StructStatVfs vfs = Os.statvfs(getContext().getFilesDir().getAbsolutePath());
+ assertTrue("/data is not mounted NOSUID", (vfs.f_flag & OsConstants.ST_NOSUID) != 0);
+ assertTrue("/data is not mounted NODEV", (vfs.f_flag & OsConstants.ST_NODEV) != 0);
+ }
+
+ @Test
+ public void testAllBlockDevicesAreSecure() throws Exception {
+ Set<File> insecure = getAllInsecureDevicesInDirAndSubdir(new File("/dev"), FileUtils.S_IFBLK);
+ assertTrue("Found insecure block devices: " + insecure.toString(),
+ insecure.isEmpty());
+ }
+
+ @Test
+ public void testDevRandomWorldReadableAndWritable() throws Exception {
+ File f = new File("/dev/random");
+
+ assertTrue(f + " cannot be opened for reading", canOpenForReading(f));
+ assertTrue(f + " cannot be opened for writing", canOpenForWriting(f));
+
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ assertTrue(FileUtils.getFileStatus(f.getPath(), status, false));
+ assertTrue(
+ f + " not world-readable/writable. Actual mode: 0"
+ + Integer.toString(status.mode, 8),
+ (status.mode & 0666) == 0666);
+ }
+
+ @Test
+ public void testDevUrandomWorldReadableAndWritable() throws Exception {
+ File f = new File("/dev/urandom");
+
+ assertTrue(f + " cannot be opened for reading", canOpenForReading(f));
+ assertTrue(f + " cannot be opened for writing", canOpenForWriting(f));
+
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ assertTrue(FileUtils.getFileStatus(f.getPath(), status, false));
+ assertTrue(
+ f + " not world-readable/writable. Actual mode: 0"
+ + Integer.toString(status.mode, 8),
+ (status.mode & 0666) == 0666);
+ }
+
+ @Test
+ public void testProcUUIDReadable() throws Exception {
+ File f = new File("/proc/sys/kernel/random/uuid");
+
+ assertTrue(f + " cannot be opened for reading", canOpenForReading(f));
+ assertFalse(f + " can be opened for writing", canOpenForWriting(f));
+
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ assertTrue(FileUtils.getFileStatus(f.getPath(), status, false));
+ assertTrue(
+ f + " not 0444. Actual mode: 0"
+ + Integer.toString(status.mode, 8),
+ (status.mode & 0666) == 0444);
+ }
+
+ @Test
+ public void testDevHwRandomLockedDown() throws Exception {
+ File f = new File("/dev/hw_random");
+ if (!f.exists()) {
+ // HW RNG is not required to be exposed on all devices.
+ return;
+ }
+
+ // SELinux policy should ensure that the file isn't visible to apps at
+ // all.
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ assertFalse("stat permitted on " + f + " (SELinux issue?)",
+ FileUtils.getFileStatus(f.getPath(), status, false));
+
+ // Double-check that we really can't read/write the file.
+ assertFalse(f + " can be opened for reading (SELinux issue?)", canOpenForReading(f));
+ assertFalse(f + " can be opened for writing (SELinux issue?)", canOpenForWriting(f));
+ }
+
+ private static boolean canOpenForReading(File f) {
+ try (InputStream in = new FileInputStream(f)) {
+ return true;
+ } catch (IOException expected) {
+ return false;
+ }
+ }
+
+ private static boolean canOpenForWriting(File f) {
+ try (OutputStream out = new FileOutputStream(f)) {
+ return true;
+ } catch (IOException expected) {
+ return false;
+ }
+ }
+
+ @Test
+ public void testFileHasOnlyCapsThrowsOnInvalidCaps() throws Exception {
+ try {
+ // Ensure negative cap id fails.
+ new FileUtils.CapabilitySet()
+ .add(-1)
+ .fileHasOnly("/system/bin/run-as");
+ fail();
+ }
+ catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ // Ensure too-large cap throws.
+ new FileUtils.CapabilitySet()
+ .add(OsConstants.CAP_LAST_CAP + 1)
+ .fileHasOnly("/system/bin/run-as");
+ fail();
+ }
+ catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Test that the /system/bin/run-as command has setuid and setgid
+ * attributes set on the file. If these calls fail, debugger
+ * breakpoints for native code will not work as run-as will not
+ * be able to perform required elevated-privilege functionality.
+ */
+ @Test
+ public void testRunAsHasCorrectCapabilities() throws Exception {
+ // ensure file is user and group read/executable
+ String filename = "/system/bin/run-as";
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ assertTrue(FileUtils.getFileStatus(filename, status, false));
+ assertTrue(status.hasModeFlag(FileUtils.S_IRUSR | FileUtils.S_IXUSR));
+ assertTrue(status.hasModeFlag(FileUtils.S_IRGRP | FileUtils.S_IXGRP));
+
+ // ensure file owner/group is set correctly
+ File f = new File(filename);
+ assertFileOwnedBy(f, "root");
+ assertFileOwnedByGroup(f, "shell");
+
+ // ensure file has setuid/setgid enabled
+ assertTrue(FileUtils.hasSetUidCapability(filename));
+ assertTrue(FileUtils.hasSetGidCapability(filename));
+
+ // ensure file has *only* setuid/setgid attributes enabled
+ assertTrue(new FileUtils.CapabilitySet()
+ .add(OsConstants.CAP_SETUID)
+ .add(OsConstants.CAP_SETGID)
+ .fileHasOnly("/system/bin/run-as"));
+ }
+
+ private static Set<File>
+ getAllInsecureDevicesInDirAndSubdir(File dir, int type) throws Exception {
+ assertTrue(dir.isDirectory());
+ Set<File> retval = new HashSet<File>();
+
+ if (isSymbolicLink(dir)) {
+ // don't examine symbolic links.
+ return retval;
+ }
+
+ File[] subDirectories = dir.listFiles(new FileFilter() {
+ @Override public boolean accept(File pathname) {
+ return pathname.isDirectory();
+ }
+ });
+
+
+ /* recurse into subdirectories */
+ if (subDirectories != null) {
+ for (File f : subDirectories) {
+ retval.addAll(getAllInsecureDevicesInDirAndSubdir(f, type));
+ }
+ }
+
+ File[] filesInThisDirectory = dir.listFiles();
+ if (filesInThisDirectory == null) {
+ return retval;
+ }
+
+ for (File f: filesInThisDirectory) {
+ FileUtils.FileStatus status = new FileUtils.FileStatus();
+ FileUtils.getFileStatus(f.getAbsolutePath(), status, false);
+ if (status.isOfType(type)) {
+ if (f.canRead() || f.canWrite() || f.canExecute()) {
+ retval.add(f);
+ }
+ if (status.uid == 2000) {
+ // The shell user should not own any devices
+ retval.add(f);
+ }
+
+ // Don't allow devices owned by GIDs
+ // accessible to non-privileged applications.
+ if ((status.gid == 1007) // AID_LOG
+ || (status.gid == 1015) // AID_SDCARD_RW
+ || (status.gid == 1023) // AID_MEDIA_RW
+ || (status.gid == 1028) // AID_SDCARD_R
+ || (status.gid == 2000)) // AID_SHELL
+ {
+ if (status.hasModeFlag(FileUtils.S_IRGRP)
+ || status.hasModeFlag(FileUtils.S_IWGRP)
+ || status.hasModeFlag(FileUtils.S_IXGRP))
+ {
+ retval.add(f);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ private Set<File> getWritableDirectoriesAndSubdirectoriesOf(File dir) throws Exception {
+ Set<File> retval = new HashSet<File>();
+ if (!dir.isDirectory()) {
+ return retval;
+ }
+
+ if (isSymbolicLink(dir)) {
+ // don't examine symbolic links.
+ return retval;
+ }
+
+ String myHome = getContext().getApplicationInfo().dataDir;
+ String thisDir = dir.getCanonicalPath();
+ if (thisDir.startsWith(myHome)) {
+ // Don't examine directories within our home directory.
+ // We expect these directories to be writable.
+ return retval;
+ }
+
+ if (isDirectoryWritable(dir)) {
+ retval.add(dir);
+ }
+
+ File[] subFiles = dir.listFiles();
+ if (subFiles == null) {
+ return retval;
+ }
+
+ for (File f : subFiles) {
+ retval.addAll(getWritableDirectoriesAndSubdirectoriesOf(f));
+ }
+
+ return retval;
+ }
+
+ private static boolean isSymbolicLink(File f) throws IOException {
+ return !f.getAbsolutePath().equals(f.getCanonicalPath());
+ }
+
+}
diff --git a/tests/cts/permission/src/android/permission/cts/FileUtils.java b/tests/cts/permission/src/android/permission/cts/FileUtils.java
new file mode 100644
index 000000000..0743cd30a
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/FileUtils.java
@@ -0,0 +1,128 @@
+package android.permission.cts;
+
+/*
+ * Copyright (C) 2010 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.
+ */
+
+import android.system.OsConstants;
+
+import com.google.common.primitives.Ints;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Bits and pieces copied from hidden API of android.os.FileUtils. */
+public class FileUtils {
+
+ public static final int S_IFMT = 0170000;
+ public static final int S_IFSOCK = 0140000;
+ public static final int S_IFLNK = 0120000;
+ public static final int S_IFREG = 0100000;
+ public static final int S_IFBLK = 0060000;
+ public static final int S_IFDIR = 0040000;
+ public static final int S_IFCHR = 0020000;
+ public static final int S_IFIFO = 0010000;
+
+ public static final int S_ISUID = 0004000;
+ public static final int S_ISGID = 0002000;
+ public static final int S_ISVTX = 0001000;
+
+ public static final int S_IRWXU = 00700;
+ public static final int S_IRUSR = 00400;
+ public static final int S_IWUSR = 00200;
+ public static final int S_IXUSR = 00100;
+
+ public static final int S_IRWXG = 00070;
+ public static final int S_IRGRP = 00040;
+ public static final int S_IWGRP = 00020;
+ public static final int S_IXGRP = 00010;
+
+ public static final int S_IRWXO = 00007;
+ public static final int S_IROTH = 00004;
+ public static final int S_IWOTH = 00002;
+ public static final int S_IXOTH = 00001;
+
+ static {
+ System.loadLibrary("ctspermission_jni");
+ }
+
+ public static class FileStatus {
+
+ public int dev;
+ public int ino;
+ public int mode;
+ public int nlink;
+ public int uid;
+ public int gid;
+ public int rdev;
+ public long size;
+ public int blksize;
+ public long blocks;
+ public long atime;
+ public long mtime;
+ public long ctime;
+
+ public boolean hasModeFlag(int flag) {
+ if (((S_IRWXU | S_IRWXG | S_IRWXO) & flag) != flag) {
+ throw new IllegalArgumentException("Inappropriate flag " + flag);
+ }
+ return (mode & flag) == flag;
+ }
+
+ public boolean isOfType(int type) {
+ if ((type & S_IFMT) != type) {
+ throw new IllegalArgumentException("Unknown type " + type);
+ }
+ return (mode & S_IFMT) == type;
+ }
+ }
+
+ public static class CapabilitySet {
+
+ private final Set<Integer> mCapabilities = new HashSet<Integer>();
+
+ public CapabilitySet add(int capability) {
+ if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
+ throw new IllegalArgumentException(String.format(
+ "capability id %d out of valid range", capability));
+ }
+ mCapabilities.add(capability);
+ return this;
+ }
+
+ private native static boolean fileHasOnly(String path,
+ int[] capabilities);
+
+ public boolean fileHasOnly(String path) {
+ return fileHasOnly(path, Ints.toArray(mCapabilities));
+ }
+ }
+
+ /**
+ * @param path of the file to stat
+ * @param status object to set the fields on
+ * @param statLinks or don't stat links (lstat vs stat)
+ * @return whether or not we were able to stat the file
+ */
+ public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
+
+ public native static String getUserName(int uid);
+
+ public native static String getGroupName(int gid);
+
+ public native static boolean hasSetUidCapability(String path);
+
+ public native static boolean hasSetGidCapability(String path);
+}
diff --git a/tests/cts/permission/src/android/permission/cts/IgnoreAllTestsRule.java b/tests/cts/permission/src/android/permission/cts/IgnoreAllTestsRule.java
new file mode 100644
index 000000000..45288bd48
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/IgnoreAllTestsRule.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts;
+
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+/**
+ * A {@link org.junit.Rule} that will cause all tests in the class
+ * to be ignored if the argument to the constructor is true.
+ */
+public class IgnoreAllTestsRule implements MethodRule {
+
+ private boolean mIgnore;
+
+ /**
+ * Creates a new IgnoreAllTestsRule
+ * @param ignore If true, all tests in the class will be ignored. If false, this rule will
+ * do nothing.
+ */
+ public IgnoreAllTestsRule(boolean ignore) {
+ mIgnore = ignore;
+ }
+
+ @Override
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ if (mIgnore) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ }
+ };
+ } else {
+ return base;
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/cts/permission/src/android/permission/cts/LocationAccessCheckTest.java
new file mode 100644
index 000000000..2bb2ed4c7
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_NOT_FOREGROUND;
+import static android.location.Criteria.ACCURACY_FINE;
+import static android.os.Process.myUserHandle;
+import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS;
+import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.waitForBroadcasts;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Process;
+import android.permission.cts.appthataccesseslocation.IAccessLocationOnCommand;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+import android.platform.test.annotations.SystemUserOnly;
+import android.platform.test.rule.ScreenRecordRule;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
+import com.android.compatibility.common.util.mainline.MainlineModule;
+import com.android.compatibility.common.util.mainline.ModuleDetector;
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Tests the {@code LocationAccessCheck} in permission controller.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Cannot set system settings as instant app. Also we never show a location "
+ + "access check notification for instant apps.")
+@ScreenRecordRule.ScreenRecord
+@FlakyTest
+public class LocationAccessCheckTest {
+
+ private static final String LOG_TAG = LocationAccessCheckTest.class.getSimpleName();
+
+ private static final String TEST_APP_PKG = "android.permission.cts.appthataccesseslocation";
+ private static final String TEST_APP_LABEL = "CtsLocationAccess";
+ private static final String TEST_APP_SERVICE = TEST_APP_PKG + ".AccessLocationOnCommand";
+ private static final String TEST_APP_LOCATION_BG_ACCESS_APK =
+ "/data/local/tmp/cts-permission/CtsAppThatAccessesLocationOnCommand.apk";
+ private static final String TEST_APP_LOCATION_FG_ACCESS_APK =
+ "/data/local/tmp/cts-permission/AppThatDoesNotHaveBgLocationAccess.apk";
+ private static final String ACTION_SET_UP_LOCATION_ACCESS_CHECK =
+ "com.android.permissioncontroller.action.SET_UP_LOCATION_ACCESS_CHECK";
+ private static final int LOCATION_ACCESS_CHECK_JOB_ID = 0;
+ private static final int LOCATION_ACCESS_CHECK_NOTIFICATION_ID = 0;
+
+ private static final String PROPERTY_LOCATION_ACCESS_CHECK_DELAY_MILLIS =
+ "location_access_check_delay_millis";
+ private static final String PROPERTY_LOCATION_ACCESS_PERIODIC_INTERVAL_MILLIS =
+ "location_access_check_periodic_interval_millis";
+ private static final String PROPERTY_BG_LOCATION_CHECK_ENABLED = "bg_location_check_is_enabled";
+
+ private static final long UNEXPECTED_TIMEOUT_MILLIS = 10000;
+ private static final long EXPECTED_TIMEOUT_MILLIS = 15000;
+ private static final long LOCATION_ACCESS_TIMEOUT_MILLIS = 15000;
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final ActivityManager sActivityManager =
+ sContext.getSystemService(ActivityManager.class);
+ private static final PackageManager sPackageManager = sContext.getPackageManager();
+ private static final AppOpsManager sAppOpsManager =
+ sContext.getSystemService(AppOpsManager.class);
+ private static final LocationManager sLocationManager =
+ sContext.getSystemService(LocationManager.class);
+ private static final UiAutomation sUiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+
+ private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager()
+ .getPermissionControllerPackageName();
+ private static final String LocationAccessCheckOnBootReceiver =
+ "com.android.permissioncontroller.permission.service"
+ + ".LocationAccessCheck$SetupPeriodicBackgroundLocationAccessCheck";
+
+
+ /**
+ * The result of {@link #assumeCanGetFineLocation()}, so we don't have to run it over and over
+ * again.
+ */
+ private static Boolean sCanAccessFineLocation = null;
+
+ private static ServiceConnection sConnection;
+ private static IAccessLocationOnCommand sLocationAccessor;
+
+ private static void assumeNotPlayManaged() throws Exception {
+ assumeFalse(ModuleDetector.moduleIsPlayManaged(
+ sContext.getPackageManager(), MainlineModule.PERMISSION_CONTROLLER));
+ }
+
+ @Rule
+ public final ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(false, false);
+
+ // Override SafetyCenter enabled flag
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigSafetyCenterEnabled =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyCenterUtils.PROPERTY_SAFETY_CENTER_ENABLED,
+ Boolean.toString(true));
+
+ // Override BG location enabled flag
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigBgLocationCheckEnabled =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_BG_LOCATION_CHECK_ENABLED,
+ Boolean.toString(true));
+
+ // Override general notification interval
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigBgCheckIntervalMillis =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_LOCATION_ACCESS_PERIODIC_INTERVAL_MILLIS,
+ "100");
+
+ // Override general delay interval
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigBgCheckDelayMillis =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_LOCATION_ACCESS_CHECK_DELAY_MILLIS,
+ "50");
+
+ private static boolean sWasLocationEnabled = true;
+
+ @BeforeClass
+ public static void beforeClassSetup() throws Exception {
+ reduceDelays();
+ allowNotificationAccess();
+ installBackgroundAccessApp();
+ runWithShellPermissionIdentity(() -> {
+ sWasLocationEnabled = sLocationManager.isLocationEnabled();
+ if (!sWasLocationEnabled) {
+ sLocationManager.setLocationEnabledForUser(true, Process.myUserHandle());
+ }
+ });
+ }
+
+ /**
+ * Change settings so that permission controller can show location access notifications more
+ * often.
+ */
+ public static void reduceDelays() {
+ runWithShellPermissionIdentity(() -> {
+ ContentResolver cr = sContext.getContentResolver();
+ // New settings will be applied in when permission controller is reset
+ Settings.Secure.putLong(cr, LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, 100);
+ Settings.Secure.putLong(cr, LOCATION_ACCESS_CHECK_DELAY_MILLIS, 50);
+ });
+ }
+
+ @AfterClass
+ public static void cleanupAfterClass() throws Throwable {
+ resetDelays();
+ uninstallTestApp();
+ disallowNotificationAccess();
+ runWithShellPermissionIdentity(() -> {
+ if (!sWasLocationEnabled) {
+ sLocationManager.setLocationEnabledForUser(false, Process.myUserHandle());
+ }
+ });
+ }
+
+ /**
+ * Reset settings so that permission controller runs normally.
+ */
+ public static void resetDelays() throws Throwable {
+ runWithShellPermissionIdentity(() -> {
+ ContentResolver cr = sContext.getContentResolver();
+ Settings.Secure.resetToDefaults(cr, LOCATION_ACCESS_CHECK_INTERVAL_MILLIS);
+ Settings.Secure.resetToDefaults(cr, LOCATION_ACCESS_CHECK_DELAY_MILLIS);
+ });
+ }
+
+ /**
+ * Connected to {@value #TEST_APP_PKG} and make it access the location in the background
+ */
+ private void accessLocation() throws Throwable {
+ if (sConnection == null || sLocationAccessor == null) {
+ bindService();
+ }
+
+ long beforeAccess = System.currentTimeMillis();
+ // Wait a little to avoid raciness in timing between threads
+ Thread.sleep(1000);
+
+ // Try again until binder call goes though. It might not go through if the sLocationAccessor
+ // is not bound yet
+ eventually(() -> {
+ assertNotNull(sLocationAccessor);
+ sLocationAccessor.accessLocation();
+ }, EXPECTED_TIMEOUT_MILLIS);
+
+ // Wait until the access is recorded
+ eventually(() -> {
+ List<AppOpsManager.PackageOps> ops = runWithShellPermissionIdentity(
+ () -> sAppOpsManager.getOpsForPackage(
+ sPackageManager.getPackageUid(TEST_APP_PKG, 0), TEST_APP_PKG,
+ OPSTR_FINE_LOCATION));
+
+ // Background access must have happened after "beforeAccess"
+ assertTrue(ops.get(0).getOps().get(0).getLastAccessBackgroundTime(OP_FLAGS_ALL_TRUSTED)
+ >= beforeAccess);
+ }, EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * A {@link java.util.concurrent.Callable} that can throw a {@link Throwable}
+ */
+ private interface ThrowingCallable<T> {
+ T call() throws Throwable;
+ }
+
+ /**
+ * A {@link Runnable} that can throw a {@link Throwable}
+ */
+ private interface ThrowingRunnable {
+ void run() throws Throwable;
+ }
+
+ /**
+ * Make sure that a {@link ThrowingRunnable} eventually finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingRunnable} to run.
+ * @param timeout the maximum time to wait
+ */
+ public static void eventually(@NonNull ThrowingRunnable r, long timeout) throws Throwable {
+ eventually(() -> {
+ r.run();
+ return 0;
+ }, timeout);
+ }
+
+ /**
+ * Make sure that a {@link ThrowingCallable} eventually finishes without throwing a {@link
+ * Exception}.
+ *
+ * @param r The {@link ThrowingCallable} to run.
+ * @param timeout the maximum time to wait
+ * @return the return value from the callable
+ * @throws NullPointerException If the return value never becomes non-null
+ */
+ public static <T> T eventually(@NonNull ThrowingCallable<T> r, long timeout) throws Throwable {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ T res = r.call();
+ if (res == null) {
+ throw new NullPointerException("No result");
+ }
+
+ return res;
+ } catch (Throwable e) {
+ if (System.currentTimeMillis() - start < timeout) {
+ Log.d(LOG_TAG, "Ignoring exception", e);
+
+ Thread.sleep(500);
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Clear all data of a package including permissions and files.
+ *
+ * @param pkg The name of the package to be cleared
+ */
+ private static void clearPackageData(@NonNull String pkg) {
+ unbindService();
+ runShellCommand("pm clear --user -2 " + pkg);
+ }
+
+ private static boolean isJobReady() {
+ String jobStatus = runShellCommand("cmd jobscheduler get-job-state -u "
+ + Process.myUserHandle().getIdentifier() + " " + PERMISSION_CONTROLLER_PKG
+ + " " + LOCATION_ACCESS_CHECK_JOB_ID);
+ return jobStatus.contains("waiting");
+ }
+
+ /**
+ * Force a run of the location check.
+ */
+ private static void runLocationCheck() throws Throwable {
+ if (!isJobReady()) {
+ PermissionUtils.scheduleJob(sUiAutomation, PERMISSION_CONTROLLER_PKG,
+ LOCATION_ACCESS_CHECK_JOB_ID, EXPECTED_TIMEOUT_MILLIS,
+ ACTION_SET_UP_LOCATION_ACCESS_CHECK, LocationAccessCheckOnBootReceiver);
+ }
+
+ TestUtils.awaitJobUntilRequestedState(
+ PERMISSION_CONTROLLER_PKG,
+ LOCATION_ACCESS_CHECK_JOB_ID,
+ EXPECTED_TIMEOUT_MILLIS,
+ sUiAutomation,
+ "waiting"
+ );
+
+ TestUtils.runJobAndWaitUntilCompleted(
+ PERMISSION_CONTROLLER_PKG,
+ LOCATION_ACCESS_CHECK_JOB_ID,
+ EXPECTED_TIMEOUT_MILLIS,
+ sUiAutomation
+ );
+ }
+
+ /**
+ * Get a location access notification that is currently visible.
+ *
+ * @param cancelNotification if {@code true} the notification is canceled inside this method
+ * @return The notification or {@code null} if there is none
+ */
+ private StatusBarNotification getNotification(boolean cancelNotification) throws Throwable {
+ return CtsNotificationListenerServiceUtils.getNotificationForPackageAndId(
+ PERMISSION_CONTROLLER_PKG, LOCATION_ACCESS_CHECK_NOTIFICATION_ID,
+ cancelNotification);
+ }
+
+ /**
+ * Grant a permission to the {@value #TEST_APP_PKG}.
+ *
+ * @param permission The permission to grant
+ */
+ private void grantPermissionToTestApp(@NonNull String permission) {
+ sUiAutomation.grantRuntimePermission(TEST_APP_PKG, permission);
+ }
+
+ /**
+ * Register {@link CtsNotificationListenerService}.
+ */
+ public static void allowNotificationAccess() {
+ runShellCommand("cmd notification allow_listener " + (new ComponentName(sContext,
+ CtsNotificationListenerService.class).flattenToString()));
+ }
+
+ public static void installBackgroundAccessApp() throws Exception {
+ String output =
+ runShellCommandOrThrow("pm install -r -g " + TEST_APP_LOCATION_BG_ACCESS_APK);
+ assertTrue(output.contains("Success"));
+ // Wait for user sensitive to be updated, which is checked by LocationAccessCheck.
+ Thread.sleep(5000);
+ }
+
+ public static void uninstallTestApp() {
+ unbindService();
+ runShellCommand("pm uninstall " + TEST_APP_PKG);
+ }
+
+ private static void unbindService() {
+ if (sConnection != null) {
+ sContext.unbindService(sConnection);
+ }
+ sConnection = null;
+ sLocationAccessor = null;
+ }
+
+ private static void installForegroundAccessApp() throws Exception {
+ unbindService();
+ runShellCommandOrThrow("pm install -r -g " + TEST_APP_LOCATION_FG_ACCESS_APK);
+ // Wait for user sensitive to be updated, which is checked by LocationAccessCheck.
+ Thread.sleep(5000);
+ }
+
+ /**
+ * Skip each test for low ram device
+ */
+ public void assumeIsNotLowRamDevice() {
+ assumeFalse(sActivityManager.isLowRamDevice());
+ }
+
+ public void wakeUpAndDismissKeyguard() {
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ runShellCommand("wm dismiss-keyguard");
+ }
+
+ public void bindService() {
+ sConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ sLocationAccessor = IAccessLocationOnCommand.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sConnection = null;
+ sLocationAccessor = null;
+ }
+ };
+
+ Intent testAppService = new Intent();
+ testAppService.setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_SERVICE));
+
+ sContext.bindService(testAppService, sConnection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND);
+ }
+
+ @Before
+ public void beforeEachTestSetup() throws Throwable {
+ assumeIsNotLowRamDevice();
+ wakeUpAndDismissKeyguard();
+ bindService();
+ resetPermissionControllerBeforeEachTest();
+ assumeCanGetFineLocation();
+ }
+
+ /**
+ * Reset the permission controllers state before each test
+ */
+ public void resetPermissionControllerBeforeEachTest() throws Throwable {
+ // Has to be before resetPermissionController to make sure enablement time is the reset time
+ // of permission controller
+ runLocationCheck();
+
+ resetPermissionController();
+
+ eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ // Reset job scheduler stats (to allow more jobs to be run)
+ runShellCommand(
+ "cmd jobscheduler reset-execution-quota -u " + myUserHandle().getIdentifier() + " "
+ + PERMISSION_CONTROLLER_PKG);
+ runShellCommand("cmd jobscheduler reset-schedule-quota");
+ }
+
+ /**
+ * Make sure fine location can be accessed at all.
+ */
+ public void assumeCanGetFineLocation() {
+ if (sCanAccessFineLocation == null) {
+ Criteria crit = new Criteria();
+ crit.setAccuracy(ACCURACY_FINE);
+
+ CountDownLatch locationCounter = new CountDownLatch(1);
+ sContext.getSystemService(LocationManager.class).requestSingleUpdate(crit,
+ new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ locationCounter.countDown();
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+ }, Looper.getMainLooper());
+
+
+ try {
+ sCanAccessFineLocation = locationCounter.await(LOCATION_ACCESS_TIMEOUT_MILLIS,
+ MILLISECONDS);
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ assumeTrue(sCanAccessFineLocation);
+ }
+
+ /**
+ * Reset the permission controllers state.
+ */
+ private static void resetPermissionController() throws Throwable {
+ unbindService();
+ PermissionUtils.resetPermissionControllerJob(sUiAutomation, PERMISSION_CONTROLLER_PKG,
+ LOCATION_ACCESS_CHECK_JOB_ID, 45000,
+ ACTION_SET_UP_LOCATION_ACCESS_CHECK, LocationAccessCheckOnBootReceiver);
+ }
+
+ /**
+ * Unregister {@link CtsNotificationListenerService}.
+ */
+ public static void disallowNotificationAccess() {
+ runShellCommand("cmd notification disallow_listener " + (new ComponentName(sContext,
+ CtsNotificationListenerService.class)).flattenToString());
+ }
+
+ @After
+ public void cleanupAfterEachTest() throws Throwable {
+ resetPrivacyConfig();
+ locationUnbind();
+ }
+
+ /**
+ * Reset location access check
+ */
+ public void resetPrivacyConfig() throws Throwable {
+ // Run a location access check to update enabled state inside permission controller
+ runLocationCheck();
+ }
+
+ public void locationUnbind() throws Throwable {
+ unbindService();
+ }
+
+ @Test
+ public void notificationIsShown() throws Throwable {
+ accessLocation();
+ runLocationCheck();
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void notificationIsShownOnlyOnce() throws Throwable {
+ assumeNotPlayManaged();
+
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+
+ accessLocation();
+ runLocationCheck();
+
+ assertNull(getNotification(true));
+ }
+
+ @SystemUserOnly(reason = "b/172259935")
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void notificationIsShownAgainAfterClear() throws Throwable {
+ assumeNotPlayManaged();
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+
+ clearPackageData(TEST_APP_PKG);
+
+ // Wait until package is cleared and permission controller has cleared the state
+ Thread.sleep(10000);
+ waitForBroadcasts();
+
+ // Clearing removed the permissions, hence grant them again
+ grantPermissionToTestApp(ACCESS_FINE_LOCATION);
+ grantPermissionToTestApp(ACCESS_BACKGROUND_LOCATION);
+
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @SystemUserOnly(reason = "b/172259935")
+ @Test
+ public void notificationIsShownAgainAfterUninstallAndReinstall() throws Throwable {
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+
+ uninstallTestApp();
+
+ // Wait until package permission controller has cleared the state
+ Thread.sleep(2000);
+
+ installBackgroundAccessApp();
+ waitForBroadcasts();
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void removeNotificationOnUninstall() throws Throwable {
+ assumeNotPlayManaged();
+
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(false)), EXPECTED_TIMEOUT_MILLIS);
+
+ uninstallTestApp();
+ // wait for permission controller (broadcast receiver) to clean up things
+ Thread.sleep(5000);
+ waitForBroadcasts();
+
+ try {
+ eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS);
+ } finally {
+ installBackgroundAccessApp();
+ }
+ }
+
+ @Test
+ public void notificationIsNotShownAfterAppDoesNotRequestLocationAnymore() throws Throwable {
+ accessLocation();
+ runLocationCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+
+ // Update to app to a version that does not request permission anymore
+ installForegroundAccessApp();
+
+ try {
+ resetPermissionController();
+
+ runLocationCheck();
+
+ // We don't expect a notification, but try to trigger one anyway
+ assertNull(getNotification(false));
+ } finally {
+ installBackgroundAccessApp();
+ }
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void noNotificationIfBlamerNotSystemOrLocationProvider() throws Throwable {
+ assumeNotPlayManaged();
+
+ // Blame the app for access from an untrusted for notification purposes package.
+ runWithShellPermissionIdentity(() -> {
+ AppOpsManager appOpsManager = sContext.getSystemService(AppOpsManager.class);
+ appOpsManager.noteProxyOpNoThrow(OPSTR_FINE_LOCATION, TEST_APP_PKG,
+ sContext.getPackageManager().getPackageUid(TEST_APP_PKG, 0));
+ });
+ runLocationCheck();
+
+ assertNull(getNotification(false));
+ }
+
+ @Test
+ // Mark as flaky until b/286874765 is fixed
+ @FlakyTest
+ @MtsIgnore
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void testOpeningLocationSettingsDoesNotTriggerAccess() throws Throwable {
+ assumeNotPlayManaged();
+
+ Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ sContext.startActivity(intent);
+
+ runLocationCheck();
+ assertNull(getNotification(false));
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void noNotificationWhenLocationNeverAccessed() throws Throwable {
+ assumeNotPlayManaged();
+
+ // Reset to clear property location_access_check_enabled_time has been already happened
+ // when resetPermissionController() invoked from before test method
+
+ runLocationCheck();
+
+ // Not expecting notification as location is not accessed and previously set
+ // LOCATION_ACCESS_CHECK_ENABLED_TIME if any is cleaned up
+ assertNull(getNotification(false));
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void notificationWhenLocationAccessed() throws Throwable {
+ assumeNotPlayManaged();
+
+ // Reset to clear property location_access_check_enabled_time has been already happened
+ // when resetPermissionController() invoked from before test method
+
+ accessLocation();
+ runLocationCheck();
+
+ // Expecting notification as accessing the location causes
+ // LOCATION_ACCESS_CHECK_ENABLED_TIME to be set
+ eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 141028068)
+ public void noNotificationWhenLocationAccessedPriorToEnableTime() throws Throwable {
+ assumeNotPlayManaged();
+
+ accessLocation();
+
+ // Reset to clear the property location_access_check_enabled_time
+ resetPermissionController();
+
+ runLocationCheck();
+
+ // Not expecting the notification as the location
+ // access was prior to LOCATION_ACCESS_CHECK_ENABLED_TIME (No notification for prior events)
+ assertNull(getNotification(false));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ public void notificationOnClickOpensSafetyCenter() throws Throwable {
+ assumeTrue(SafetyCenterUtils.deviceSupportsSafetyCenter(sContext));
+ accessLocation();
+ runLocationCheck();
+
+ StatusBarNotification currentNotification = eventually(() -> {
+ StatusBarNotification notification = getNotification(false);
+ assertNotNull(notification);
+ return notification;
+ }, EXPECTED_TIMEOUT_MILLIS);
+
+ // Verify content intent
+ PendingIntent contentIntent = currentNotification.getNotification().contentIntent;
+ if (SdkLevel.isAtLeastU()) {
+ contentIntent.send(null, 0, null, null, null, null,
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
+ } else {
+ contentIntent.send();
+ }
+
+ SafetyCenterUtils.assertSafetyCenterStarted();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java b/tests/cts/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
new file mode 100644
index 000000000..adac0befa
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/MainlineNetworkStackPermissionTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MainlineNetworkStackPermissionTest{
+ private final Context mContext = InstrumentationRegistry.getContext();
+
+ /**
+ * Test that a package defining android.permission.MAINLINE_NETWORK_STACK is installed,
+ * and is a system package
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot access PackageManager#getPermissionInfo")
+ public void testPackageWithMainlineNetworkStackPermission() throws Exception {
+ final PackageManager packageManager = mContext.getPackageManager();
+ assertNotNull("Unable to find PackageManager.", packageManager);
+
+ final PermissionInfo permissioninfo =
+ packageManager.getPermissionInfo(PERMISSION_MAINLINE_NETWORK_STACK, 0);
+ assertNotNull("Network stack permission is not defined.", permissioninfo);
+
+ PackageInfo packageInfo = packageManager.getPackageInfo(permissioninfo.packageName,
+ PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_PERMISSIONS);
+ assertNotNull("Package defining the network stack permission is not a system package.",
+ packageInfo.permissions);
+
+ for (PermissionInfo permission : packageInfo.permissions) {
+ if (PERMISSION_MAINLINE_NETWORK_STACK.equals(permission.name)) {
+ return;
+ }
+ }
+
+ fail("Expect a system package defining " + PERMISSION_MAINLINE_NETWORK_STACK
+ + " is installed.");
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/MinMaxSdkVersionTest.kt b/tests/cts/permission/src/android/permission/cts/MinMaxSdkVersionTest.kt
new file mode 100644
index 000000000..4679fbee8
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/MinMaxSdkVersionTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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 android.permission.cts
+
+import android.app.Instrumentation
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import android.permission.cts.PermissionUtils.install
+import android.platform.test.annotations.AppModeFull
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import androidx.test.uiautomator.UiDevice
+import com.android.compatibility.common.util.CddTest
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@CddTest(requirement = "9.1/C-0-1")
+@AppModeFull(reason = "Instant apps cannot read state of other packages.")
+class MinMaxSdkVersionTest {
+ private val mInstrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private var mUiDevice: UiDevice? = null
+ private var mContext: Context? = null
+
+ @Before
+ fun setup() {
+ mUiDevice = UiDevice.getInstance(mInstrumentation)
+ mContext = mInstrumentation?.targetContext
+ }
+
+ @Test
+ fun givenMinSdkVersionInRangeThenPermissionIsRequested() {
+ install(TEST_APP_PATH)
+
+ Assert.assertTrue(appRequestsPermission(MINSDK_LT_DEVICESDK))
+ }
+
+ @Test
+ fun givenMinSdkVersionOutOfRangeThenPermissionIsNotRequested() {
+ install(TEST_APP_PATH)
+
+ Assert.assertFalse(appRequestsPermission(MINSDK_GT_DEVICESDK))
+ }
+
+ @Test
+ fun givenMaxSdkVersionInRangeThenPermissionIsRequested() {
+ install(TEST_APP_PATH)
+
+ Assert.assertTrue(appRequestsPermission(MAXSDK_GT_DEVICESDK))
+ }
+
+ @Test
+ fun givenMaxSdkVersionOutOfRangeThenPermissionIsNotRequested() {
+ install(TEST_APP_PATH)
+
+ Assert.assertFalse(appRequestsPermission(MAXSDK_LT_DEVICESDK))
+ }
+
+ private fun appRequestsPermission(permName: String): Boolean {
+ val packageInfo =
+ mContext!!
+ .packageManager
+ .getPackageInfo(TEST_APP_PKG_NAME, PackageManager.GET_PERMISSIONS)
+ return packageInfo.requestedPermissions!!.any { it == permName }
+ }
+
+ companion object {
+ private const val TEST_APP_NAME = "CtsAppThatRequestsMultiplePermissionsWithMinMaxSdk.apk"
+ private const val TMP_DIR = "/data/local/tmp/cts-permission/"
+ private const val TEST_APP_PATH = TMP_DIR + TEST_APP_NAME
+ private const val TEST_APP_PKG_NAME = "android.permission.cts.appthatrequestpermission"
+ private const val CUSTOM_PERMS = "$TEST_APP_PKG_NAME.permissions"
+ private const val MINSDK_LT_DEVICESDK = "$CUSTOM_PERMS.MINSDK_LT_DEVICESDK"
+ private const val MINSDK_GT_DEVICESDK = "$CUSTOM_PERMS.MINSDK_GT_DEVICESDK"
+ private const val MAXSDK_LT_DEVICESDK = "$CUSTOM_PERMS.MAXSDK_LT_DEVICESDK"
+ private const val MAXSDK_GT_DEVICESDK = "$CUSTOM_PERMS.MAXSDK_GT_DEVICESDK"
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java
new file mode 100644
index 000000000..d4ad2ad04
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.permission.cts.PermissionUtils.grantPermission;
+import static android.permission.cts.PermissionUtils.install;
+import static android.permission.cts.PermissionUtils.revokePermission;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.bluetooth.cts.EnableBluetoothRule;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.EnableLocationRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests the behavior of the
+ * {@link android.Manifest.permission_group#NEARBY_DEVICES} permission group
+ * under various permutations of grant states.
+ *
+ * Note that some tests will be recognized as known failures with the new permission subsystem
+ * until b/273999500 is fixed.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+public class NearbyDevicesPermissionTest {
+ private static final String TEST_APP_PKG = "android.permission.cts.appthatrequestpermission";
+ private static final String TEST_APP_AUTHORITY = "appthatrequestpermission";
+ private static final String DISAVOWAL_APP_PKG = "android.permission.cts.appneverforlocation";
+
+ private static final String TMP_DIR = "/data/local/tmp/cts-permission/";
+ private static final String APK_BLUETOOTH_30 = TMP_DIR
+ + "CtsAppThatRequestsBluetoothPermission30.apk";
+ private static final String APK_BLUETOOTH_31 = TMP_DIR
+ + "CtsAppThatRequestsBluetoothPermission31.apk";
+ private static final String APK_BLUETOOTH_NEVER_FOR_LOCATION_31 = TMP_DIR
+ + "CtsAppThatRequestsBluetoothPermissionNeverForLocation31.apk";
+ private static final String APK_BLUETOOTH_NEVER_FOR_LOCATION_NO_PROVIDER = TMP_DIR
+ + "CtsAppThatRequestsBluetoothPermissionNeverForLocationNoProvider.apk";
+
+ private enum Result {
+ UNKNOWN, EXCEPTION, EMPTY, FILTERED, FULL
+ }
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ @ClassRule
+ public static final EnableBluetoothRule sEnableBluetoothRule = new EnableBluetoothRule(true);
+
+ @Rule
+ public final EnableLocationRule enableLocationRule = new EnableLocationRule();
+
+ public void uninstallTestApp() {
+ uninstallApp(TEST_APP_PKG);
+ uninstallApp(DISAVOWAL_APP_PKG);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallTestApp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallTestApp();
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermission30_Default() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_30);
+ assertScanBluetoothResult(Result.EMPTY);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermission30_GrantLocation() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_30);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.FULL);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermission31_Default() throws Throwable {
+ install(APK_BLUETOOTH_31);
+ assertScanBluetoothResult(Result.EXCEPTION);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermission31_GrantNearby() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_31);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ assertScanBluetoothResult(Result.EMPTY);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermission31_GrantLocation() throws Throwable {
+ install(APK_BLUETOOTH_31);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.EXCEPTION);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermission31_GrantNearby_GrantLocation() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_31);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.FULL);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermissionNeverForLocation31_Default() throws Throwable {
+ install(APK_BLUETOOTH_NEVER_FOR_LOCATION_31);
+ assertScanBluetoothResult(Result.EXCEPTION);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermissionNeverForLocation31_GrantNearby() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_NEVER_FOR_LOCATION_31);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ assertScanBluetoothResult(Result.FILTERED);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermissionNeverForLocation31_GrantLocation() throws Throwable {
+ install(APK_BLUETOOTH_NEVER_FOR_LOCATION_31);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.EXCEPTION);
+ }
+
+ @Test
+ @CddTest(requirement = "7.4.3/C-6-1")
+ public void testRequestBluetoothPermissionNeverForLocation31_GrantNearby_GrantLocation()
+ throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_NEVER_FOR_LOCATION_31);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.FILTERED);
+ }
+
+ @Test
+ public void testRequestBluetoothPermission31_OnBehalfOfDisavowingApp() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_31);
+ install(APK_BLUETOOTH_NEVER_FOR_LOCATION_NO_PROVIDER);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ grantPermission(DISAVOWAL_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(DISAVOWAL_APP_PKG, BLUETOOTH_SCAN);
+ assertScanBluetoothResult("PROXY", Result.FILTERED);
+ }
+
+ /**
+ * Verify that a legacy app that was unable to interact with Bluetooth
+ * devices is still unable to interact with them after updating to a modern
+ * SDK; they'd always need to involve the user to gain permissions.
+ */
+ @Test
+ public void testRequestBluetoothPermission_Default_Upgrade() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_30);
+ assertScanBluetoothResult(Result.EMPTY);
+
+ // Upgrading to target a new SDK level means they need to explicitly
+ // request the new runtime permission; by default it's denied
+ install(APK_BLUETOOTH_NEVER_FOR_LOCATION_31);
+ assertScanBluetoothResult(Result.EXCEPTION);
+
+ // If the user does grant it, they can scan again
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ assertScanBluetoothResult(Result.FILTERED);
+ }
+
+ /**
+ * Verify that a legacy app that was able to interact with Bluetooth devices
+ * is still able to interact with them after updating to a modern SDK.
+ */
+ @Test
+ public void testRequestBluetoothPermission_GrantLocation_Upgrade() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_30);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.FULL);
+
+ // Upgrading to target a new SDK level means they still have the access
+ // they enjoyed as a legacy app
+ install(APK_BLUETOOTH_31);
+ assertScanBluetoothResult(Result.FULL);
+ }
+
+ /**
+ * Verify that downgrading an app doesn't gain them any access to Bluetooth
+ * scan results; they'd always need to involve the user to gain permissions.
+ */
+ @Test
+ public void testRequestBluetoothPermission_Downgrade() throws Throwable {
+ assumeTrue(supportsBluetoothLe());
+
+ install(APK_BLUETOOTH_31);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ grantPermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ grantPermission(TEST_APP_PKG, ACCESS_FINE_LOCATION);
+ grantPermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ assertScanBluetoothResult(Result.FULL);
+
+ // Revoking nearby permission means modern app can't scan
+ revokePermission(TEST_APP_PKG, BLUETOOTH_CONNECT);
+ revokePermission(TEST_APP_PKG, BLUETOOTH_SCAN);
+ assertScanBluetoothResult(Result.EXCEPTION);
+
+ // And if they attempt to downgrade, confirm that they can't obtain the
+ // split-permission grant from the older non-runtime permissions
+ install(APK_BLUETOOTH_30);
+ assertScanBluetoothResult(Result.EXCEPTION);
+ }
+
+ private void assertScanBluetoothResult(Result expected) {
+ assertScanBluetoothResult(null, expected);
+ }
+
+ private void assertScanBluetoothResult(String arg, Result expected) {
+ SystemClock.sleep(1000); // Wait for location permissions to propagate
+ final ContentResolver resolver = InstrumentationRegistry.getTargetContext()
+ .getContentResolver();
+ final Bundle res = resolver.call(TEST_APP_AUTHORITY, "", arg, null);
+ Result actual = Result.values()[res.getInt(Intent.EXTRA_INDEX)];
+ assertEquals(expected, actual);
+ }
+
+ private boolean supportsBluetoothLe() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
+ }
+
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java b/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
new file mode 100644
index 000000000..aeb4d1d28
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NearbyDevicesRenouncePermissionTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.AppOpsManager;
+import android.app.AsyncNotedAppOp;
+import android.app.SyncNotedAppOp;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.cts.EnableBluetoothRule;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanResult;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.content.ContextParams;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.provider.DeviceConfig;
+import android.util.ArraySet;
+import android.util.Base64;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
+import com.android.compatibility.common.util.EnableLocationRule;
+import com.android.compatibility.common.util.SystemUtil;
+
+import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Tests behaviour when performing bluetooth scans with renounced location permission.
+ */
+public class NearbyDevicesRenouncePermissionTest {
+
+ private static final String TAG = "NearbyDevicesRenouncePermissionTest";
+ private static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan";
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ @ClassRule
+ public static final EnableBluetoothRule sEnableBluetoothRule = new EnableBluetoothRule(true);
+
+ @Rule
+ public DeviceConfigStateChangerRule safetyLabelChangeNotificationsEnabledConfig =
+ new DeviceConfigStateChangerRule(
+ mContext,
+ DeviceConfig.NAMESPACE_BLUETOOTH,
+ "scan_quota_count",
+ Integer.toString(1000)
+ );
+
+ @Rule
+ public final EnableLocationRule enableLocationRule = new EnableLocationRule();
+
+ private AppOpsManager mAppOpsManager;
+
+ private volatile long mTestStartTimestamp;
+ private final AtomicInteger mLocationNoteCount = new AtomicInteger(0);
+ private final AtomicInteger mScanNoteCount = new AtomicInteger(0);
+
+ private enum Result {
+ UNKNOWN, EXCEPTION, EMPTY, FILTERED, FULL
+ }
+
+ private enum Scenario {
+ DEFAULT, RENOUNCE, RENOUNCE_MIDDLE, RENOUNCE_END
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ // Sleep to guarantee that past noteOp timestamps are less than mTestStartTimestamp
+ Uninterruptibles.sleepUninterruptibly(2, TimeUnit.MILLISECONDS);
+ mTestStartTimestamp = System.currentTimeMillis();
+
+ mLocationNoteCount.set(0);
+ mScanNoteCount.set(0);
+
+ mAppOpsManager = getApplicationContext().getSystemService(AppOpsManager.class);
+ mAppOpsManager.setOnOpNotedCallback(getApplicationContext().getMainExecutor(),
+ new AppOpsManager.OnOpNotedCallback() {
+ @Override
+ public void onNoted(SyncNotedAppOp op) {
+ switch (op.getOp()) {
+ case OPSTR_FINE_LOCATION:
+ logNoteOp(op);
+ mLocationNoteCount.incrementAndGet();
+ break;
+ case OPSTR_BLUETOOTH_SCAN:
+ logNoteOp(op);
+ mScanNoteCount.incrementAndGet();
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void onSelfNoted(SyncNotedAppOp op) {
+ }
+
+ @Override
+ public void onAsyncNoted(AsyncNotedAppOp asyncOp) {
+ switch (asyncOp.getOp()) {
+ case OPSTR_FINE_LOCATION:
+ logNoteOp(asyncOp);
+ if (asyncOp.getTime() < mTestStartTimestamp) {
+ Log.i(TAG, "ignoring asyncOp that originated before test "
+ + "start");
+ return;
+ }
+ mLocationNoteCount.incrementAndGet();
+ break;
+ case OPSTR_BLUETOOTH_SCAN:
+ logNoteOp(asyncOp);
+ if (asyncOp.getTime() < mTestStartTimestamp) {
+ Log.i(TAG, "ignoring asyncOp that originated before test "
+ + "start");
+ return;
+ }
+ mScanNoteCount.incrementAndGet();
+ break;
+ default:
+ }
+ }
+ });
+ }
+
+ private void logNoteOp(SyncNotedAppOp op) {
+ Log.i(TAG, "OnOpNotedCallback::onNoted(op=" + op.getOp() + ")");
+ }
+
+ private void logNoteOp(AsyncNotedAppOp asyncOp) {
+ Log.i(TAG, "OnOpNotedCallback::"
+ + "onAsyncNoted(op=" + asyncOp.getOp()
+ + ", testStartTimestamp=" + mTestStartTimestamp
+ + ", noteOpTimestamp=" + asyncOp.getTime() + ")");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mAppOpsManager.setOnOpNotedCallback(null, null);
+ }
+
+ @AppModeFull
+ @Test
+ public void scanWithoutRenouncingNotesBluetoothAndLocation() throws Exception {
+ assumeTrue(supportsBluetoothLe());
+
+ assertThat(performScan(Scenario.DEFAULT)).isEqualTo(Result.FULL);
+ SystemUtil.eventually(() -> {
+ assertThat(mLocationNoteCount.get()).isGreaterThan(0);
+ assertThat(mScanNoteCount.get()).isGreaterThan(0);
+ });
+ }
+
+ @AppModeFull
+ @Test
+ public void scanRenouncingLocationNotesBluetoothButNotLocation() throws Exception {
+ assumeTrue(supportsBluetoothLe());
+
+ assertThat(performScan(Scenario.RENOUNCE)).isEqualTo(Result.FILTERED);
+ SystemUtil.eventually(() -> {
+ assertThat(mLocationNoteCount.get()).isEqualTo(0);
+ assertThat(mScanNoteCount.get()).isGreaterThan(0);
+ });
+ }
+
+ @AppModeFull
+ @Test
+ public void scanRenouncingInMiddleOfChainNotesBluetoothButNotLocation() throws Exception {
+ assumeTrue(supportsBluetoothLe());
+
+ assertThat(performScan(Scenario.RENOUNCE_MIDDLE)).isEqualTo(Result.FILTERED);
+ SystemUtil.eventually(() -> {
+ assertThat(mLocationNoteCount.get()).isEqualTo(0);
+ assertThat(mScanNoteCount.get()).isGreaterThan(0);
+ });
+ }
+
+ @AppModeFull
+ @Test
+ public void scanRenouncingAtEndOfChainNotesBluetoothButNotLocation() throws Exception {
+ assertThat(performScan(Scenario.RENOUNCE_END)).isEqualTo(Result.FILTERED);
+ SystemUtil.eventually(() -> {
+ assertThat(mLocationNoteCount.get()).isEqualTo(0);
+ assertThat(mScanNoteCount.get()).isGreaterThan(0);
+ });
+ }
+
+ private Result performScan(Scenario scenario) {
+ try {
+ Context context = createContext(scenario, getApplicationContext());
+
+ final BluetoothManager bm = context.getSystemService(BluetoothManager.class);
+ final BluetoothLeScanner scanner = bm.getAdapter().getBluetoothLeScanner();
+
+ final HashSet<String> observed = new HashSet<>();
+
+ ScanCallback callback = new ScanCallback() {
+ public void onScanResult(int callbackType, ScanResult result) {
+ Log.v(TAG, String.valueOf(result));
+ observed.add(Base64.encodeToString(result.getScanRecord().getBytes(), 0));
+ }
+
+ public void onBatchScanResults(List<ScanResult> results) {
+ for (ScanResult result : results) {
+ onScanResult(0, result);
+ }
+ }
+ };
+ scanner.startScan(callback);
+
+ // Wait a few seconds to figure out what we actually observed
+ SystemClock.sleep(3000);
+ scanner.stopScan(callback);
+ switch (observed.size()) {
+ case 0:
+ return Result.EMPTY;
+ case 1:
+ return Result.FILTERED;
+ case 5:
+ return Result.FULL;
+ default:
+ return Result.UNKNOWN;
+ }
+ } catch (Throwable t) {
+ Log.v(TAG, "Failed to scan", t);
+ return Result.EXCEPTION;
+ }
+ }
+
+ private Context createContext(Scenario scenario, Context context) throws Exception {
+ if (scenario == Scenario.DEFAULT) {
+ return context;
+ }
+
+ Set<String> renouncedPermissions = new ArraySet<>();
+ renouncedPermissions.add(ACCESS_FINE_LOCATION);
+
+ switch (scenario) {
+ case RENOUNCE:
+ return SystemUtil.callWithShellPermissionIdentity(() ->
+ context.createContext(
+ new ContextParams.Builder()
+ .setRenouncedPermissions(renouncedPermissions)
+ .setAttributionTag(context.getAttributionTag())
+ .build())
+ );
+ case RENOUNCE_MIDDLE:
+ AttributionSource nextAttrib = new AttributionSource(
+ Process.SHELL_UID, "com.android.shell", null, (Set<String>) null, null);
+ return SystemUtil.callWithShellPermissionIdentity(() ->
+ context.createContext(
+ new ContextParams.Builder()
+ .setRenouncedPermissions(renouncedPermissions)
+ .setAttributionTag(context.getAttributionTag())
+ .setNextAttributionSource(nextAttrib)
+ .build())
+ );
+ case RENOUNCE_END:
+ nextAttrib = new AttributionSource(
+ Process.SHELL_UID, "com.android.shell", null, renouncedPermissions, null);
+ return SystemUtil.callWithShellPermissionIdentity(() ->
+ context.createContext(
+ new ContextParams.Builder()
+ .setAttributionTag(context.getAttributionTag())
+ .setNextAttributionSource(nextAttrib)
+ .build())
+ );
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ private boolean supportsBluetoothLe() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java
new file mode 100644
index 000000000..1b3f65ee6
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NfcPermissionTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.Executor;
+
+@RunWith(JUnit4.class)
+public final class NfcPermissionTest {
+
+ private NfcAdapter mNfcAdapter;
+ private static final String PKG_NAME = "com.android.packagename";
+
+ private boolean supportsHardware() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+ }
+
+ @Before
+ public void setUp() {
+ assumeTrue(supportsHardware());
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(InstrumentationRegistry.getTargetContext());
+ }
+
+ /**
+ * Verifies that isControllerAlwaysOnSupported() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+ */
+ @Test
+ @AppModeFull
+ public void testIsControllerAlwaysOnSupported() {
+ try {
+ mNfcAdapter.isControllerAlwaysOnSupported();
+ fail("mNfcAdapter.isControllerAlwaysOnSupported() did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that isControllerAlwaysOn() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+ */
+ @Test
+ @AppModeFull
+ public void testIsControllerAlwaysOn() {
+ try {
+ mNfcAdapter.isControllerAlwaysOn();
+ fail("mNfcAdapter.isControllerAlwaysOn() did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that setControllerAlwaysOn(true) requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+ */
+ @Test
+ @AppModeFull
+ public void testSetControllerAlwaysOnTrue() {
+ try {
+ mNfcAdapter.setControllerAlwaysOn(true);
+ fail("mNfcAdapter.setControllerAlwaysOn(true) did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that setControllerAlwaysOn(false) requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+ */
+ @Test
+ @AppModeFull
+ public void testSetControllerAlwaysOnFalse() {
+ try {
+ mNfcAdapter.setControllerAlwaysOn(false);
+ fail("mNfcAdapter.setControllerAlwaysOn(true) did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that registerControllerAlwaysOnListener() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+ */
+ @Test
+ @AppModeFull
+ public void testRegisterControllerAlwaysOnListener() {
+ try {
+ mNfcAdapter.registerControllerAlwaysOnListener(
+ new SynchronousExecutor(), new AlwaysOnStateListener());
+ fail("mNfcAdapter.registerControllerAlwaysOnListener did not throw"
+ + "SecurityException as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that unregisterControllerAlwaysOnListener() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+ */
+ @Test
+ @AppModeFull
+ public void testUnregisterControllerAlwaysOnListener() {
+ try {
+ mNfcAdapter.unregisterControllerAlwaysOnListener(new AlwaysOnStateListener());
+ fail("mNfcAdapter.unregisterControllerAlwaysOnListener did not throw"
+ + "SecurityException as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that isTagIntentAppPreferenceSupported() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission.WRITE_SECURE_SETTINGS}.
+ */
+ @Test
+ @AppModeFull
+ public void testIsTagIntentAppPreferenceSupported() {
+ try {
+ mNfcAdapter.isTagIntentAppPreferenceSupported();
+ fail("mNfcAdapter.isTagIntentAppPreferenceSupported() did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that getTagIntentAppPreferenceForUser() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission.WRITE_SECURE_SETTINGS}.
+ */
+ @Test
+ @AppModeFull
+ public void testGetTagIntentAppPreferenceForUser() {
+ try {
+ mNfcAdapter.getTagIntentAppPreferenceForUser(ActivityManager.getCurrentUser());
+ fail("mNfcAdapter.getTagIntentAppPreferenceForUser() did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ /**
+ * Verifies that setTagIntentAppPreferenceForUser() requires Permission.
+ * <p>
+ * Requires Permission: {@link android.Manifest.permission.WRITE_SECURE_SETTINGS}.
+ */
+ @Test
+ @AppModeFull
+ public void testSetTagIntentAppPreferenceForUser() {
+ try {
+ mNfcAdapter.setTagIntentAppPreferenceForUser(ActivityManager.getCurrentUser(),
+ PKG_NAME, true);
+ fail("mNfcAdapter.setTagIntentAppPreferenceForUser() did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException se) {
+ // Expected Exception
+ }
+ }
+
+ private class SynchronousExecutor implements Executor {
+ public void execute(Runnable r) {
+ r.run();
+ }
+ }
+
+ private class AlwaysOnStateListener implements ControllerAlwaysOnListener {
+ @Override
+ public void onControllerAlwaysOnChanged(boolean isEnabled) {
+ // Do nothing
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
new file mode 100644
index 000000000..3d9ba8214
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoActivityRelatedPermissionTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+
+import androidx.test.filters.MediumTest;
+
+import java.util.List;
+
+/**
+ * Verify the Activity related operations require specific permissions.
+ */
+public class NoActivityRelatedPermissionTest
+ extends ActivityInstrumentationTestCase2<PermissionStubActivity> {
+
+ private PermissionStubActivity mActivity;
+
+ public NoActivityRelatedPermissionTest() {
+ super("android.permission.cts", PermissionStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ }
+
+ /**
+ * Verify that get task requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#GET_TASKS}
+ */
+ @MediumTest
+ public void testGetTask() {
+ ActivityManager manager = (ActivityManager) getActivity()
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ List<ActivityManager.RunningTaskInfo> runningTasks = manager.getRunningTasks(10);
+ // Current implementation should only return tasks for home and the caller. Since there can
+ // be multiple home tasks, we remove them from the list and then check that there is one or
+ // less task left in the list.
+ removeHomeRunningTasks(runningTasks);
+ assertTrue("Found tasks: " + runningTasks,
+ runningTasks == null || runningTasks.size() <= 1);
+
+ List<ActivityManager.RecentTaskInfo> recentTasks = manager.getRecentTasks(10,
+ ActivityManager.RECENT_WITH_EXCLUDED);
+ // Current implementation should only return tasks for home and the caller. Since there can
+ // be multiple home tasks, we remove them from the list and then check that there is one or
+ // less task left in the list.
+ removeHomeRecentsTasks(recentTasks);
+ assertTrue("Found tasks: " + recentTasks, recentTasks == null || recentTasks.size() <= 1);
+ }
+
+ private void removeHomeRecentsTasks(List<ActivityManager.RecentTaskInfo> tasks) {
+ for (int i = tasks.size() -1; i >= 0; i--) {
+ ActivityManager.RecentTaskInfo task = tasks.get(i);
+ if (task.baseIntent != null && isHomeIntent(task.baseIntent)) {
+ tasks.remove(i);
+ }
+ }
+ }
+
+ private void removeHomeRunningTasks(List<ActivityManager.RunningTaskInfo> tasks) {
+ for (int i = tasks.size() -1; i >= 0; i--) {
+ ActivityManager.RunningTaskInfo task = tasks.get(i);
+ if (task.baseIntent != null && isHomeIntent(task.baseIntent)) {
+ tasks.remove(i);
+ }
+ }
+ }
+
+ private boolean isHomeIntent(Intent intent) {
+ return Intent.ACTION_MAIN.equals(intent.getAction())
+ && (intent.hasCategory(Intent.CATEGORY_HOME)
+ || intent.hasCategory(Intent.CATEGORY_SECONDARY_HOME))
+ && intent.getCategories().size() == 1
+ && intent.getData() == null
+ && intent.getType() == null;
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoAudioPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoAudioPermissionTest.java
new file mode 100644
index 000000000..50498b1d5
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoAudioPermissionTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify the audio related operations require specific permissions.
+ */
+public class NoAudioPermissionTest extends AndroidTestCase {
+ private static final String TAG = NoAudioPermissionTest.class.getSimpleName();
+ private AudioManager mAudioManager;
+ private static final int MODE_COUNT = 3;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull(mAudioManager);
+ }
+
+ private boolean hasMicrophone() {
+ return getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_MICROPHONE);
+ }
+
+ /**
+ * Verify that AudioManager.setMicrophoneMute, AudioManager.setMode requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ */
+ @SmallTest
+ public void testSetMicrophoneMute() {
+ boolean muteState = mAudioManager.isMicrophoneMute();
+ int originalMode = mAudioManager.getMode();
+ // If there is no permission of MODIFY_AUDIO_SETTINGS, setMicrophoneMute does nothing.
+ if (muteState) {
+ Log.w(TAG, "Mic seems muted by hardware! Please unmute and rerrun the test.");
+ } else {
+ mAudioManager.setMicrophoneMute(!muteState);
+ assertEquals(muteState, mAudioManager.isMicrophoneMute());
+ }
+
+ // If there is no permission of MODIFY_AUDIO_SETTINGS, setMode does nothing.
+ assertTrue(AudioManager.MODE_NORMAL != AudioManager.MODE_RINGTONE);
+
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ assertEquals(originalMode, mAudioManager.getMode());
+
+ mAudioManager.setMode(AudioManager.MODE_RINGTONE);
+ assertEquals(originalMode, mAudioManager.getMode());
+ }
+
+ /**
+ * Verify that AudioManager routing methods require permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+ */
+ @SuppressWarnings("deprecation")
+ @SmallTest
+ public void testRouting() {
+
+ // If there is no permission of MODIFY_AUDIO_SETTINGS, setSpeakerphoneOn does nothing.
+ boolean prevState = mAudioManager.isSpeakerphoneOn();
+ mAudioManager.setSpeakerphoneOn(!prevState);
+ assertEquals(prevState, mAudioManager.isSpeakerphoneOn());
+
+ // If there is no permission of MODIFY_AUDIO_SETTINGS, setBluetoothScoOn does nothing.
+ prevState = mAudioManager.isBluetoothScoOn();
+ mAudioManager.setBluetoothScoOn(!prevState);
+ assertEquals(prevState, mAudioManager.isBluetoothScoOn());
+ }
+
+ /**
+ * Verify that {@link android.media.AudioRecord.Builder#build} and
+ * {@link android.media.AudioRecord#AudioRecord} require permission
+ * {@link android.Manifest.permission#RECORD_AUDIO}.
+ */
+ @SmallTest
+ public void testRecordPermission() {
+ if (!hasMicrophone()) return;
+
+ // test builder
+ assertThrows(java.lang.UnsupportedOperationException.class, () -> {
+ final AudioRecord record = new AudioRecord.Builder().build();
+ record.release();
+ });
+
+ // test constructor
+ final int sampleRate = 8000;
+ final int halfSecondInBytes = sampleRate;
+ AudioRecord record = new AudioRecord(
+ MediaRecorder.AudioSource.DEFAULT, sampleRate, AudioFormat.CHANNEL_IN_MONO,
+ AudioFormat.ENCODING_PCM_16BIT, halfSecondInBytes);
+ final int state = record.getState();
+ record.release();
+ assertEquals(AudioRecord.STATE_UNINITIALIZED, state);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoBroadcastPackageRemovedPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoBroadcastPackageRemovedPermissionTest.java
new file mode 100644
index 000000000..1a46842b2
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoBroadcastPackageRemovedPermissionTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify Context related methods without specific BROADCAST series permissions.
+ */
+public class NoBroadcastPackageRemovedPermissionTest extends AndroidTestCase {
+ private static final String TEST_RECEIVER_PERMISSION = "receiverPermission";
+
+ /**
+ * Verify that Context#sendStickyBroadcast(Intent),
+ * Context#removeStickyBroadcast(Intent)
+ * requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#BROADCAST_STICKY }.
+ */
+ @SmallTest
+ public void testSendOrRemoveStickyBroadcast() {
+ try {
+ mContext.sendStickyBroadcast(createIntent(Intent.ACTION_WALLPAPER_CHANGED));
+ fail("Context.sendStickyBroadcast did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mContext.removeStickyBroadcast(createIntent(Intent.ACTION_WALLPAPER_CHANGED));
+ fail("Context.removeStickyBroadcast did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that Context#sendBroadcast(Intent),
+ * Context#sendBroadcast(Intent, String)
+ * Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver,
+ * Handler, int, String, Bundle)
+ * Context#sendOrderedBroadcast(Intent, String) with ACTION_UID_REMOVED
+ * with ACTION_PACKAGE_REMOVED requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#BROADCAST_PACKAGE_REMOVED}.
+ */
+ @SmallTest
+ public void testSendBroadcast() {
+ try {
+ mContext.sendBroadcast(createIntent(Intent.ACTION_PACKAGE_REMOVED));
+ fail("Context.sendBroadcast did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mContext.sendBroadcast(createIntent(Intent.ACTION_PACKAGE_REMOVED),
+ TEST_RECEIVER_PERMISSION);
+ fail("Context.sendBroadcast did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mContext.sendOrderedBroadcast(createIntent(Intent.ACTION_PACKAGE_REMOVED),
+ TEST_RECEIVER_PERMISSION, null, null, 0, "initialData", Bundle.EMPTY);
+ fail("Context.sendOrderedBroadcast did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mContext.sendOrderedBroadcast(createIntent(Intent.ACTION_PACKAGE_REMOVED),
+ TEST_RECEIVER_PERMISSION);
+ fail("Context.sendOrderedBroadcast did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ private Intent createIntent(String action) {
+ Intent intent = new Intent();
+ intent.setAction(action);
+ return intent;
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoCaptureVideoPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoCaptureVideoPermissionTest.java
new file mode 100644
index 000000000..e0573044c
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoCaptureVideoPermissionTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.test.AndroidTestCase;
+import android.util.DisplayMetrics;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify the capture system video output permission requirements.
+ */
+public class NoCaptureVideoPermissionTest extends AndroidTestCase {
+ private static final String NAME = "VirtualDisplayTest";
+ private static final int WIDTH = 720;
+ private static final int HEIGHT = 480;
+ private static final int DENSITY = DisplayMetrics.DENSITY_MEDIUM;
+
+ /**
+ * Verify that DisplayManager.createVirtualDisplay() requires permissions to
+ * create public displays.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT} or
+ * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT}.
+ */
+ @SmallTest
+ public void testCreatePublicVirtualDisplay() {
+ DisplayManager displayManager =
+ (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+ ImageReader reader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBX_8888, 1);
+ try {
+ displayManager.createVirtualDisplay(NAME, WIDTH, HEIGHT, DENSITY,
+ reader.getSurface(), DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC);
+ fail("DisplayManager.createVirtualDisplay() didn't throw SecurityException "
+ + "as expected when creating public virtual display.");
+ } catch (SecurityException e) {
+ // expected
+ } finally {
+ reader.close();
+ }
+ }
+
+ /**
+ * Verify that DisplayManager.createVirtualDisplay() requires permissions to
+ * create secure displays.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT}.
+ */
+ @SmallTest
+ public void testCreateSecureVirtualDisplay() {
+ DisplayManager displayManager =
+ (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+ ImageReader reader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBX_8888, 1);
+ try {
+ displayManager.createVirtualDisplay(NAME, WIDTH, HEIGHT, DENSITY,
+ reader.getSurface(), DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE);
+ fail("DisplayManager.createVirtualDisplay() didn't throw SecurityException "
+ + "as expected when creating secure virtual display.");
+ } catch (SecurityException e) {
+ // expected
+ } finally {
+ reader.close();
+ }
+ }
+
+ /**
+ * Verify that DisplayManager.createVirtualDisplay() does not requires permissions to
+ * create private displays.
+ */
+ @SmallTest
+ public void testCreatePrivateVirtualDisplay() {
+ DisplayManager displayManager =
+ (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
+ ImageReader reader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBX_8888, 1);
+ try {
+ VirtualDisplay display = displayManager.createVirtualDisplay(
+ NAME, WIDTH, HEIGHT, DENSITY,
+ reader.getSurface(), 0);
+ display.release();
+ } catch (SecurityException e) {
+ fail("DisplayManager.createVirtualDisplay() should not throw SecurityException "
+ + "when creating private virtual display.");
+ } finally {
+ reader.close();
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoKeyPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoKeyPermissionTest.java
new file mode 100644
index 000000000..ac77947d9
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoKeyPermissionTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify the key input related operations require specific permissions.
+ */
+public class NoKeyPermissionTest extends AndroidTestCase {
+ KeyguardManager mKeyManager;
+ KeyguardManager.KeyguardLock mKeyLock;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mKeyManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ if (mKeyManager != null) {
+ mKeyLock = mKeyManager.newKeyguardLock("testTag");
+ }
+ }
+
+ /**
+ * Verify that KeyguardManager.KeyguardLock.disableKeyguard requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#DISABLE_KEYGUARD}.
+ */
+ @SmallTest
+ public void testDisableKeyguard() {
+ // KeyguardManager was not accessible, pass.
+ if (mKeyManager == null) {
+ return;
+ }
+ try {
+ mKeyLock.disableKeyguard();
+ fail("KeyguardManager.KeyguardLock.disableKeyguard did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that KeyguardManager.KeyguardLock.reenableKeyguard requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#DISABLE_KEYGUARD}.
+ */
+ @SmallTest
+ public void testReenableKeyguard() {
+ // KeyguardManager was not accessible, pass.
+ if (mKeyManager == null) {
+ return;
+ }
+ try {
+ mKeyLock.reenableKeyguard();
+ fail("KeyguardManager.KeyguardLock.reenableKeyguard did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that KeyguardManager.exitKeyguardSecurely requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#DISABLE_KEYGUARD}.
+ */
+ @SmallTest
+ public void testExitKeyguardSecurely() {
+ // KeyguardManager was not accessible, pass.
+ if (mKeyManager == null) {
+ return;
+ }
+ try {
+ mKeyManager.exitKeyguardSecurely(null);
+ fail("KeyguardManager.exitKeyguardSecurely did not throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
new file mode 100644
index 000000000..5dc73d520
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+import java.net.InetAddress;
+
+/**
+ * Verify ConnectivityManager related methods without specific network state permissions.
+ */
+public class NoNetworkStatePermissionTest extends AndroidTestCase {
+ private ConnectivityManager mConnectivityManager;
+ private static final int TEST_NETWORK_TYPE = ConnectivityManager.TYPE_MOBILE;
+ private static final String TEST_FEATURE = "enableHIPRI";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ assertNotNull(mConnectivityManager);
+ }
+
+ /**
+ * Verify that ConnectivityManager#getActiveNetworkInfo() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ */
+ @SmallTest
+ public void testGetActiveNetworkInfo() {
+ try {
+ mConnectivityManager.getActiveNetworkInfo();
+ fail("ConnectivityManager.getActiveNetworkInfo didn't throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that ConnectivityManager#getNetworkInfo() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ */
+ @SmallTest
+ public void testGetNetworkInfo() {
+ try {
+ mConnectivityManager.getNetworkInfo(TEST_NETWORK_TYPE);
+ fail("ConnectivityManager.getNetworkInfo didn't throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that ConnectivityManager#getAllNetworkInfo() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ */
+ @SmallTest
+ public void testGetAllNetworkInfo() {
+ try {
+ mConnectivityManager.getAllNetworkInfo();
+ fail("ConnectivityManager.getAllNetworkInfo didn't throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testSecurityExceptionFromDns() throws Exception {
+ try {
+ InetAddress.getByName("www.google.com");
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoReadLogsPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoReadLogsPermissionTest.java
new file mode 100644
index 000000000..f0d70b2ce
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoReadLogsPermissionTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.MediumTest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * Verify the read system log require specific permissions.
+ */
+public class NoReadLogsPermissionTest extends AndroidTestCase {
+ /**
+ * Verify that we'll only get our logs without the READ_LOGS permission.
+ *
+ * We test this by examining the logs looking for ActivityManager lines.
+ * Since ActivityManager runs as a different UID, we shouldn't see
+ * any of those log entries.
+ *
+ * @throws IOException
+ */
+ @MediumTest
+ public void testLogcat() throws IOException {
+ Process logcatProc = null;
+ BufferedReader reader = null;
+ try {
+ logcatProc = Runtime.getRuntime().exec(new String[]
+ {"logcat", "-v", "brief", "-d", "ActivityManager:* *:S" });
+
+ reader = new BufferedReader(new InputStreamReader(logcatProc.getInputStream()));
+
+ int lineCt = 0;
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (!line.startsWith("--------- beginning of ")) {
+ lineCt++;
+ }
+ }
+
+ // no permission get an empty log buffer.
+ // Logcat returns only one line:
+ // "--------- beginning of <log device>"
+
+ assertEquals("Unexpected logcat entries. Are you running the "
+ + "the latest logger.c from the Android kernel?",
+ 0, lineCt);
+
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+ }
+
+ public void testEventsLogSane() throws ErrnoException {
+ testLogIsSane("/dev/log/events");
+ }
+
+ public void testMainLogSane() throws ErrnoException {
+ testLogIsSane("/dev/log/main");
+ }
+
+ public void testRadioLogSane() throws ErrnoException {
+ testLogIsSane("/dev/log/radio");
+ }
+
+ public void testSystemLogSane() throws ErrnoException {
+ testLogIsSane("/dev/log/system");
+ }
+
+ private static void testLogIsSane(String log) throws ErrnoException {
+ try {
+ StructStat stat = Os.stat(log);
+ assertEquals("not owned by uid=0", 0, stat.st_uid);
+ assertEquals("not owned by gid=logs", "log", FileUtils.getGroupName(stat.st_gid));
+ } catch (ErrnoException e) {
+ if (e.errno != OsConstants.ENOENT && e.errno != OsConstants.EACCES) {
+ throw e;
+ }
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoRollbackPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoRollbackPermissionTest.java
new file mode 100644
index 000000000..50b84fa70
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoRollbackPermissionTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.pm.PackageInstaller;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+@AppModeFull(reason = "PackageInstaller cannot be accessed by instant apps")
+public class NoRollbackPermissionTest {
+ @Test
+ public void testCreateInstallSessionWithReasonRollbackFails() throws Exception {
+ // The INSTALL_REASON_ROLLBACK allows an APK to be rolled back to a previous signing key
+ // without setting the ROLLBACK capability in the lineage. Since only signature|privileged
+ // apps can hold the necessary permission to initiate a rollback ensure apps without this
+ // permission cannot set rollback as the install reason.
+ PackageInstaller packageInstaller =
+ InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager()
+ .getPackageInstaller();
+ PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ parentParams.setRequestDowngrade(true);
+ parentParams.setMultiPackage();
+ // The constant PackageManager.INSTALL_REASON_ROLLBACK is hidden from apps, but an app can
+ // still use its constant value.
+ parentParams.setInstallReason(5);
+ assertThrows(SecurityException.class, () -> packageInstaller.createSession(parentParams));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoSystemFunctionPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoSystemFunctionPermissionTest.java
new file mode 100644
index 000000000..437aa19c4
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoSystemFunctionPermissionTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.permission.cts;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.os.Vibrator;
+import android.os.VibratorManager;
+import android.platform.test.annotations.AppModeFull;
+import android.telephony.gsm.SmsManager;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.TimeZone;
+
+/**
+ * Verify the system function require specific permissions.
+ */
+@SuppressWarnings("deprecation")
+public class NoSystemFunctionPermissionTest extends AndroidTestCase {
+
+ /**
+ * Verify that ActivityManager.restartPackage() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#RESTART_PACKAGES}.
+ */
+ @SmallTest
+ public void testRestartPackage() {
+ ActivityManager activityManager = (ActivityManager) mContext.getSystemService(
+ Context.ACTIVITY_SERVICE);
+
+ try {
+ activityManager.restartPackage("packageName");
+ fail("ActivityManager.restartPackage() didn't throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that AlarmManager.setTimeZone() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SET_TIME_ZONE}.
+ */
+ @SmallTest
+ public void testSetTimeZone() {
+ AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
+ Context.ALARM_SERVICE);
+ String[] timeZones = TimeZone.getAvailableIDs();
+ String timeZone = timeZones[0];
+
+ try {
+ alarmManager.setTimeZone(timeZone);
+ fail("AlarmManager.setTimeZone() did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that setting wallpaper relate methods require permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SET_WALLPAPER}.
+ * @throws IOException
+ */
+ @AppModeFull(reason = "Instant apps cannot access the WallpaperManager")
+ @SmallTest
+ public void testSetWallpaper() throws IOException {
+ if (!WallpaperManager.getInstance(mContext).isWallpaperSupported()) {
+ return;
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
+
+ try {
+ mContext.setWallpaper(bitmap);
+ fail("Context.setWallpaper(BitMap) did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mContext.setWallpaper((InputStream) null);
+ fail("Context.setWallpaper(InputStream) did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mContext.clearWallpaper();
+ fail("Context.clearWallpaper() did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that Vibrator's vibrating related methods requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#VIBRATE}.
+ */
+ @SmallTest
+ public void testVibrator() {
+ Vibrator vibrator = mContext.getSystemService(VibratorManager.class).getDefaultVibrator();
+
+ if (!vibrator.hasVibrator()) {
+ // Run the test only if a vibrator is present.
+ return;
+ }
+
+ if (!vibrator.hasVibrator()) {
+ // If the test device does not have a vibrator, then abort test.
+ return;
+ }
+
+ try {
+ vibrator.cancel();
+ fail("Vibrator.cancel() did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ vibrator.vibrate(1);
+ fail("Vibrator.vibrate(long) did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ long[] testPattern = {1, 1, 1, 1, 1};
+
+ try {
+ vibrator.vibrate(testPattern, 1);
+ fail("Vibrator.vibrate(long[], int) not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that sending sms requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SMS}.
+ */
+ @SmallTest
+ public void testSendSms() {
+ if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ SmsManager smsManager = SmsManager.getDefault();
+ byte[] testData = new byte[10];
+ try {
+ smsManager.sendDataMessage("1233", "1233", (short) 0, testData, null, null);
+ fail("SmsManager.sendDataMessage() did not throw SecurityException as expected.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoWakeLockPermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoWakeLockPermissionTest.java
new file mode 100644
index 000000000..95c4da727
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoWakeLockPermissionTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.permission.cts;
+
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.PowerManager;
+import android.platform.test.annotations.AppModeFull;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify the Wake Lock related operations require specific permissions.
+ */
+public class NoWakeLockPermissionTest extends AndroidTestCase {
+ private PowerManager mPowerManager;
+
+ private PowerManager.WakeLock mWakeLock;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "tag");
+ }
+
+ /**
+ * Verify that WifiManager.WifiLock.acquire() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#WAKE_LOCK}.
+ */
+ @AppModeFull(reason = "Instant apps cannot access the WifiManager")
+ @SmallTest
+ public void testWifiLockAcquire() {
+ if (!mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI)) {
+ return;
+ }
+
+ final WifiManager wifiManager = (WifiManager) mContext.getSystemService(
+ Context.WIFI_SERVICE);
+ final WifiLock wifiLock = wifiManager.createWifiLock("WakeLockPermissionTest");
+ try {
+ wifiLock.acquire();
+ fail("WifiManager.WifiLock.acquire() didn't throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that MediaPlayer.start() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#WAKE_LOCK}.
+ */
+ @SmallTest
+ public void testMediaPlayerWakeLock() {
+ final MediaPlayer mediaPlayer = new MediaPlayer();
+ mediaPlayer.setWakeMode(mContext, PowerManager.FULL_WAKE_LOCK);
+ try {
+ mediaPlayer.start();
+ fail("MediaPlayer.setWakeMode() did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ mediaPlayer.stop();
+ }
+
+ /**
+ * Verify that PowerManager.WakeLock.acquire() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#WAKE_LOCK}.
+ */
+ @SmallTest
+ public void testPowerManagerWakeLockAcquire() {
+ try {
+ mWakeLock.acquire();
+ fail("WakeLock.acquire() did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that PowerManager.WakeLock.acquire(long) requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#WAKE_LOCK}.
+ */
+ @SmallTest
+ public void testPowerManagerWakeLockAcquire2() {
+ // Tset acquire(long)
+ try {
+ mWakeLock.acquire(1);
+ fail("WakeLock.acquire(long) did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoWallpaperPermissionsTest.java b/tests/cts/permission/src/android/permission/cts/NoWallpaperPermissionsTest.java
new file mode 100644
index 000000000..fc1d6b59f
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoWallpaperPermissionsTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.permission.cts;
+
+import static android.app.WallpaperManager.FLAG_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+import static org.junit.Assert.assertThrows;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.platform.test.annotations.AppModeFull;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.function.ThrowingRunnable;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * Verify that Wallpaper-related operations enforce the correct permissions.
+ */
+@AppModeFull(reason = "instant apps cannot access the WallpaperManager")
+public class NoWallpaperPermissionsTest extends AndroidTestCase {
+ private WallpaperManager mWM;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mWM = (WallpaperManager) mContext.getSystemService(Context.WALLPAPER_SERVICE);
+ }
+
+ /**
+ * Verify that the setResource(...) methods enforce the SET_WALLPAPER permission
+ */
+ @SmallTest
+ public void testSetResource() throws IOException {
+ if (wallpaperNotSupported()) {
+ return;
+ }
+
+ try {
+ mWM.setResource(R.drawable.robot);
+ fail("WallpaperManager.setResource(id) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+
+ try {
+ mWM.setResource(R.drawable.robot, FLAG_LOCK);
+ fail("WallpaperManager.setResource(id, which) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+ }
+
+ /**
+ * Verify that the setBitmap(...) methods enforce the SET_WALLPAPER permission
+ */
+ @SmallTest
+ public void testSetBitmap() throws IOException {
+ if (wallpaperNotSupported()) {
+ return;
+ }
+
+ Bitmap b = Bitmap.createBitmap(160, 120, Bitmap.Config.RGB_565);
+
+ try {
+ mWM.setBitmap(b);
+ fail("setBitmap(b) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+
+ try {
+ mWM.setBitmap(b, null, false);
+ fail("setBitmap(b, crop, allowBackup) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+
+ try {
+ mWM.setBitmap(b, null, false, FLAG_SYSTEM);
+ fail("setBitmap(b, crop, allowBackup, which) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+ }
+
+ /**
+ * Verify that the setStream(...) methods enforce the SET_WALLPAPER permission
+ */
+ @SmallTest
+ public void testSetStream() throws IOException {
+ if (wallpaperNotSupported()) {
+ return;
+ }
+
+ ByteArrayInputStream stream = new ByteArrayInputStream(new byte[32]);
+
+ try {
+ mWM.setStream(stream);
+ fail("setStream(stream) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+
+ try {
+ mWM.setStream(stream, null, false);
+ fail("setStream(stream, crop, allowBackup) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+
+ try {
+ mWM.setStream(stream, null, false, FLAG_LOCK);
+ fail("setStream(stream, crop, allowBackup, which) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+ }
+
+ /**
+ * Verify that the clearWallpaper(...) methods enforce the SET_WALLPAPER permission
+ */
+ @SmallTest
+ public void testClearWallpaper() throws IOException {
+ if (wallpaperNotSupported()) {
+ return;
+ }
+
+ try {
+ mWM.clear();
+ fail("clear() did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+
+ try {
+ mWM.clear(FLAG_SYSTEM);
+ fail("clear(which) did not enforce SET_WALLPAPER");
+ } catch (SecurityException expected) { /* expected */ }
+ }
+
+ /**
+ * Verify that reading the current wallpaper enforce the READ_WALLPAPER_INTERNAL permission.
+ * The methods concerned are:
+ * getDrawable, peekDrawable, getFastDrawable, peekFastDrawable, getWallpaperFile.
+ *
+ * These methods should throw a SecurityException, even if MANAGE_EXTERNAL_STORAGE is granted.
+ */
+ public void testReadWallpaper() {
+ if (wallpaperNotSupported()) {
+ return;
+ }
+ String message = "with no permissions, getDrawable should throw a SecurityException";
+ assertSecurityException(mWM::getDrawable, message);
+ assertSecurityException(() -> mWM.getDrawable(FLAG_SYSTEM), message);
+ assertSecurityException(() -> mWM.getDrawable(FLAG_LOCK), message);
+
+ message = "with no permissions, peekDrawable should throw a SecurityException";
+ assertSecurityException(mWM::peekDrawable, message);
+ assertSecurityException(() -> mWM.peekDrawable(FLAG_SYSTEM), message);
+ assertSecurityException(() -> mWM.peekDrawable(FLAG_LOCK), message);
+
+ message = "with no permissions, getFastDrawable should throw a SecurityException";
+ assertSecurityException(mWM::getFastDrawable, message);
+ assertSecurityException(() -> mWM.getFastDrawable(FLAG_SYSTEM), message);
+ assertSecurityException(() -> mWM.getFastDrawable(FLAG_LOCK), message);
+
+ message = "with no permissions, peekFastDrawable should throw a SecurityException";
+ assertSecurityException(mWM::peekFastDrawable, message);
+ assertSecurityException(() -> mWM.peekFastDrawable(FLAG_SYSTEM), message);
+ assertSecurityException(() -> mWM.peekFastDrawable(FLAG_LOCK), message);
+
+ message = "with no permissions, getWallpaperFile should throw a SecurityException";
+ assertSecurityException(() -> mWM.getWallpaperFile(FLAG_SYSTEM), message);
+ assertSecurityException(() -> mWM.getWallpaperFile(FLAG_LOCK), message);
+ }
+
+ // ---------- Utility methods ----------
+
+ private boolean wallpaperNotSupported() {
+ return !(mWM.isWallpaperSupported() && mWM.isSetWallpaperAllowed());
+ }
+
+ private void assertSecurityException(ThrowingRunnable runnable, String errorMessage) {
+ assertThrows(errorMessage, SecurityException.class, runnable);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java b/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java
new file mode 100644
index 000000000..9fff22747
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NoWifiStatePermissionTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Verify WifiManager related methods without specific Wifi state permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot access the WifiManager")
+@SmallTest
+public class NoWifiStatePermissionTest {
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ private static final int TEST_NET_ID = 1;
+ private static final WifiConfiguration TEST_WIFI_CONFIGURATION = new WifiConfiguration();
+ private WifiManager mWifiManager;
+
+ @Before
+ public void setUp() {
+ boolean hasWifi = sContext.getPackageManager().hasSystemFeature(FEATURE_WIFI);
+ assumeTrue(hasWifi);
+
+ mWifiManager = (WifiManager) sContext.getSystemService(Context.WIFI_SERVICE);
+ assertNotNull(mWifiManager);
+ }
+
+ /**
+ * Verify that WifiManager#getWifiState() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testGetWifiState() {
+ mWifiManager.getWifiState();
+ }
+
+ /**
+ * Verify that WifiManager#getConfiguredNetworks() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testGetConfiguredNetworks() {
+ mWifiManager.getConfiguredNetworks();
+ }
+
+ /**
+ * Verify that WifiManager#getConnectionInfo() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testGetConnectionInfo() {
+ mWifiManager.getConnectionInfo();
+ }
+
+ /**
+ * Verify that WifiManager#getScanResults() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testGetScanResults() {
+ mWifiManager.getScanResults();
+ }
+
+ /**
+ * Verify that WifiManager#getDhcpInfo() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testGetDhcpInfo() {
+ mWifiManager.getDhcpInfo();
+ }
+
+ /**
+ * Verify that WifiManager#disconnect() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testDisconnect() {
+ mWifiManager.disconnect();
+ }
+
+ /**
+ * Verify that WifiManager#reconnect() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testReconnect() {
+ mWifiManager.reconnect();
+ }
+
+ /**
+ * Verify that WifiManager#reassociate() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testReassociate() {
+ mWifiManager.reassociate();
+ }
+
+ /**
+ * Verify that WifiManager#addNetwork() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testAddNetwork() {
+ mWifiManager.addNetwork(TEST_WIFI_CONFIGURATION);
+ }
+
+ /**
+ * Verify that WifiManager#updateNetwork() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testUpdateNetwork() {
+ TEST_WIFI_CONFIGURATION.networkId = 2;
+ mWifiManager.updateNetwork(TEST_WIFI_CONFIGURATION);
+ }
+ /**
+ * Verify that WifiManager#removeNetwork() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testRemoveNetwork() {
+ mWifiManager.removeNetwork(TEST_NET_ID);
+ }
+
+ /**
+ * Verify that WifiManager#enableNetwork() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testEnableNetwork() {
+ mWifiManager.enableNetwork(TEST_NET_ID, false);
+ }
+
+ /**
+ * Verify that WifiManager#disableNetwork() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testDisableNetwork() {
+ mWifiManager.disableNetwork(TEST_NET_ID);
+ }
+
+ /**
+ * Verify that WifiManager#pingSupplicant() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testPingSupplicant() {
+ mWifiManager.pingSupplicant();
+ }
+
+ /**
+ * Verify that WifiManager#startScan() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartScan() {
+ mWifiManager.startScan();
+ }
+
+ /**
+ * Verify that WifiManager#setWifiEnabled() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_WIFI_STATE}.
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiEnabled() {
+ mWifiManager.setWifiEnabled(true);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NotificationListenerCheckTest.java b/tests/cts/permission/src/android/permission/cts/NotificationListenerCheckTest.java
new file mode 100644
index 000000000..19fc20de6
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NotificationListenerCheckTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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 android.permission.cts;
+
+import static android.permission.cts.PermissionUtils.clearAppState;
+import static android.permission.cts.PermissionUtils.install;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+import static android.permission.cts.TestUtils.ensure;
+import static android.permission.cts.TestUtils.eventually;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.rule.ScreenRecordRule;
+import android.service.notification.StatusBarNotification;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests the {@code NotificationListenerCheck} in permission controller.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Cannot set system settings as instant app. Also we never show a notification"
+ + " listener check notification for instant apps.")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+@ScreenRecordRule.ScreenRecord
+@FlakyTest
+public class NotificationListenerCheckTest extends BaseNotificationListenerCheckTest {
+
+ public final ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(false, false);
+
+ @Before
+ public void setup() throws Throwable {
+ // Skip tests if safety center not allowed
+ assumeDeviceSupportsSafetyCenter();
+
+ wakeUpAndDismissKeyguard();
+ resetPermissionControllerBeforeEachTest();
+
+ // Cts NLS is required to verify sent Notifications, however, we don't want it to show up in
+ // testing
+ triggerAndDismissCtsNotificationListenerNotification();
+
+ clearNotifications();
+
+ // Install and allow the app with NLS for testing
+ install(TEST_APP_NOTIFICATION_LISTENER_APK);
+ allowTestAppNotificationListenerService();
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ // Disallow and uninstall the app with NLS for testing
+ disallowTestAppNotificationListenerService();
+ uninstallApp(TEST_APP_PKG);
+
+ clearNotifications();
+ }
+
+ @Test
+ public void noNotificationIfFeatureDisabled() throws Throwable {
+ setNotificationListenerCheckEnabled(false);
+
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void noNotificationIfSafetyCenterDisabled() throws Throwable {
+ SafetyCenterUtils.setSafetyCenterEnabled(false);
+
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsShown() throws Throwable {
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull("Expected notification, none found", getNotification(false)),
+ UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsShownOnlyOnce() throws Throwable {
+ runNotificationListenerCheck();
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ runNotificationListenerCheck();
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsShownAgainAfterClear() throws Throwable {
+ runNotificationListenerCheck();
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ clearAppState(TEST_APP_PKG);
+ // Wait until package is cleared and permission controller has cleared the state
+ Thread.sleep(2000);
+
+ allowTestAppNotificationListenerService();
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsShownAgainAfterUninstallAndReinstall() throws Throwable {
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ uninstallApp(TEST_APP_PKG);
+
+ // Wait until package permission controller has cleared the state
+ Thread.sleep(2000);
+
+ install(TEST_APP_NOTIFICATION_LISTENER_APK);
+
+ allowTestAppNotificationListenerService();
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsShownAgainAfterDisableAndReenableAppNotificationListener()
+ throws Throwable {
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ // Disallow NLS, and run NLS check job. This should clear NLS off notified list
+ disallowTestAppNotificationListenerService();
+ runNotificationListenerCheck();
+
+ // Re-allow NLS, and run NLS check job. This work now that it's cleared NLS off notified
+ // list
+ allowTestAppNotificationListenerService();
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void removeNotificationOnUninstall() throws Throwable {
+ runNotificationListenerCheck();
+
+ eventually(() -> assertNotNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS);
+
+ uninstallApp(TEST_APP_PKG);
+
+ // Wait until package permission controller has cleared the state
+ Thread.sleep(2000);
+
+ eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationIsNotShownAfterDisableAppNotificationListener() throws Throwable {
+ disallowTestAppNotificationListenerService();
+
+ runNotificationListenerCheck();
+
+ // We don't expect a notification, but try to trigger one anyway
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void notificationOnClick_opensSafetyCenter() throws Throwable {
+ runNotificationListenerCheck();
+
+ StatusBarNotification currentNotification = eventually(
+ () -> {
+ StatusBarNotification notification = getNotification(false);
+ assertNotNull(notification);
+ return notification;
+ }, UNEXPECTED_TIMEOUT_MILLIS);
+
+ // Verify content intent
+ PendingIntent contentIntent = currentNotification.getNotification().contentIntent;
+ if (SdkLevel.isAtLeastU()) {
+ contentIntent.send(null, 0, null, null, null, null,
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
+ } else {
+ contentIntent.send();
+ }
+
+ SafetyCenterUtils.assertSafetyCenterStarted();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/NotificationListenerCheckWithSafetyCenterUnsupportedTest.java b/tests/cts/permission/src/android/permission/cts/NotificationListenerCheckWithSafetyCenterUnsupportedTest.java
new file mode 100644
index 000000000..a346de6fd
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/NotificationListenerCheckWithSafetyCenterUnsupportedTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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 android.permission.cts;
+
+import static android.permission.cts.PermissionUtils.install;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+import static android.permission.cts.TestUtils.ensure;
+
+import static org.junit.Assert.assertNull;
+
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests the {@code NotificationListenerCheck} in permission controller.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Cannot set system settings as instant app. Also we never show a notification"
+ + " listener check notification for instant apps.")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+public class NotificationListenerCheckWithSafetyCenterUnsupportedTest
+ extends BaseNotificationListenerCheckTest {
+
+ @Before
+ public void setup() throws Throwable {
+ // Skip tests if safety center is supported
+ assumeDeviceDoesNotSupportSafetyCenter();
+
+ wakeUpAndDismissKeyguard();
+ resetPermissionControllerBeforeEachTest();
+
+ clearNotifications();
+
+ // Install and allow the app with NLS for testing
+ install(TEST_APP_NOTIFICATION_LISTENER_APK);
+ allowTestAppNotificationListenerService();
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ // Disallow and uninstall the app with NLS for testing
+ disallowTestAppNotificationListenerService();
+ uninstallApp(TEST_APP_PKG);
+
+ clearNotifications();
+ }
+
+ @Test
+ public void noNotifications_featureEnabled_safetyCenterEnabled() throws Throwable {
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void noNotifications_featureDisabled_safetyCenterEnabled() throws Throwable {
+ setNotificationListenerCheckEnabled(false);
+
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void noNotifications_featureEnabled_safetyCenterDisabled() throws Throwable {
+ SafetyCenterUtils.setSafetyCenterEnabled(false);
+
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void noNotifications_featureDisabled_safetyCenterDisabled() throws Throwable {
+ setNotificationListenerCheckEnabled(false);
+ SafetyCenterUtils.setSafetyCenterEnabled(false);
+
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ ENSURE_NOTIFICATION_NOT_SHOWN_EXPECTED_TIMEOUT_MILLIS);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
new file mode 100644
index 000000000..f5f222f80
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/OneTimePermissionTest.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.CAMERA;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assume.assumeFalse;
+
+import android.app.ActivityManager;
+import android.app.DreamManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.AsbSecurityTest;
+import android.platform.test.rule.ScreenRecordRule;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.compatibility.common.util.FeatureUtil;
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.UiAutomatorUtils2;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@ScreenRecordRule.ScreenRecord
+public class OneTimePermissionTest {
+
+ private static final String APP_PKG_NAME = "android.permission.cts.appthatrequestpermission";
+ private static final String CUSTOM_CAMERA_PERM_APP_PKG_NAME =
+ "android.permission.cts.appthatrequestcustomcamerapermission";
+ private static final String APK =
+ "/data/local/tmp/cts-permission/CtsAppThatRequestsOneTimePermission.apk";
+ private static final String CUSTOM_CAMERA_PERM_APK =
+ "/data/local/tmp/cts-permission/CtsAppThatRequestCustomCameraPermission.apk";
+ private static final String EXTRA_FOREGROUND_SERVICE_LIFESPAN =
+ "android.permission.cts.OneTimePermissionTest.EXTRA_FOREGROUND_SERVICE_LIFESPAN";
+ private static final String EXTRA_FOREGROUND_SERVICE_STICKY =
+ "android.permission.cts.OneTimePermissionTest.EXTRA_FOREGROUND_SERVICE_STICKY";
+
+ public static final String CUSTOM_PERMISSION = "appthatrequestcustomcamerapermission.CUSTOM";
+
+ private static final long ONE_TIME_TIMEOUT_MILLIS = 5000;
+ private static final long ONE_TIME_KILLED_DELAY_MILLIS = 5000;
+ private static final long ONE_TIME_TIMER_LOWER_GRACE_PERIOD = 1000;
+ private static final long ONE_TIME_TIMER_UPPER_GRACE_PERIOD = 10000;
+
+ private final Context mContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ private final PackageManager mPackageManager = mContext.getPackageManager();
+ private final UiDevice mUiDevice =
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ private final ActivityManager mActivityManager =
+ mContext.getSystemService(ActivityManager.class);
+ private String mOldOneTimePermissionTimeoutValue;
+ private String mOldOneTimePermissionKilledDelayValue;
+
+ @Rule
+ public final ScreenRecordRule sScreenRecordRule = new ScreenRecordRule(false, false);
+
+ @Rule
+ public IgnoreAllTestsRule mIgnoreAutomotive = new IgnoreAllTestsRule(
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
+
+ @Before
+ public void wakeUpScreen() {
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+ SystemUtil.runShellCommand("input keyevent 82");
+ }
+
+ @Before
+ public void installApp() {
+ runShellCommandOrThrow("pm install -r " + APK);
+ runShellCommandOrThrow("pm install -r " + CUSTOM_CAMERA_PERM_APK);
+ }
+
+ @Before
+ public void prepareDeviceForOneTime() {
+ runWithShellPermissionIdentity(() -> {
+ mOldOneTimePermissionTimeoutValue = DeviceConfig.getProperty("permissions",
+ "one_time_permissions_timeout_millis");
+ mOldOneTimePermissionKilledDelayValue = DeviceConfig.getProperty("permissions",
+ "one_time_permissions_killed_delay_millis");
+ DeviceConfig.setProperty("permissions", "one_time_permissions_timeout_millis",
+ Long.toString(ONE_TIME_TIMEOUT_MILLIS), false);
+ DeviceConfig.setProperty("permissions",
+ "one_time_permissions_killed_delay_millis",
+ Long.toString(ONE_TIME_KILLED_DELAY_MILLIS), false);
+ });
+ }
+
+ @After
+ public void uninstallApp() {
+ runShellCommand("pm uninstall " + APP_PKG_NAME);
+ runShellCommand("pm uninstall " + CUSTOM_CAMERA_PERM_APP_PKG_NAME);
+ }
+
+ @After
+ public void restoreDeviceForOneTime() {
+ runWithShellPermissionIdentity(
+ () -> {
+ DeviceConfig.setProperty("permissions", "one_time_permissions_timeout_millis",
+ mOldOneTimePermissionTimeoutValue, false);
+ DeviceConfig.setProperty("permissions",
+ "one_time_permissions_killed_delay_millis",
+ mOldOneTimePermissionKilledDelayValue, false);
+ });
+ }
+
+ @Test
+ public void testOneTimePermission() throws Throwable {
+ startApp();
+
+ CompletableFuture<Long> exitTime = registerAppExitListener();
+
+ clickOneTimeButton();
+
+ exitApp();
+
+ assertGranted(5000);
+
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD);
+
+ assertExpectedLifespan(exitTime, ONE_TIME_TIMEOUT_MILLIS);
+ }
+
+ @Ignore
+ @Test
+ public void testForegroundServiceMaintainsPermission() throws Throwable {
+ startApp();
+
+ CompletableFuture<Long> exitTime = registerAppExitListener();
+
+ clickOneTimeButton();
+
+ long expectedLifespanMillis = 2 * ONE_TIME_TIMEOUT_MILLIS;
+ startAppForegroundService(expectedLifespanMillis, false);
+
+ exitApp();
+
+ assertGranted(5000);
+
+ assertDenied(expectedLifespanMillis + ONE_TIME_TIMER_UPPER_GRACE_PERIOD);
+
+ assertExpectedLifespan(exitTime, expectedLifespanMillis);
+
+ }
+
+ @Test
+ public void testPermissionRevokedOnKill() throws Throwable {
+ startApp();
+
+ clickOneTimeButton();
+
+ exitApp();
+
+ assertGranted(5000);
+
+ mUiDevice.waitForIdle();
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ mActivityManager.killBackgroundProcesses(APP_PKG_NAME));
+
+ runWithShellPermissionIdentity(
+ () -> Thread.sleep(DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
+ "one_time_permissions_killed_delay_millis", 5000L)));
+ assertDenied(500);
+ }
+
+ @Test
+ @FlakyTest
+ public void testStickyServiceMaintainsPermissionOnRestart() throws Throwable {
+ startApp();
+
+ clickOneTimeButton();
+
+ startAppForegroundService(2 * ONE_TIME_TIMEOUT_MILLIS, true);
+
+ exitApp();
+
+ assertGranted(5000);
+ mUiDevice.waitForIdle();
+ Thread.sleep(ONE_TIME_TIMEOUT_MILLIS);
+
+ runShellCommand("am crash " + APP_PKG_NAME);
+
+ eventually(() -> runWithShellPermissionIdentity(() -> {
+ if (mActivityManager.getPackageImportance(APP_PKG_NAME) <= IMPORTANCE_CACHED) {
+ throw new AssertionError("App was never killed");
+ }
+ }));
+
+ eventually(() -> runWithShellPermissionIdentity(() -> {
+ if (mActivityManager.getPackageImportance(APP_PKG_NAME)
+ > IMPORTANCE_FOREGROUND_SERVICE) {
+ throw new AssertionError("Foreground service never resumed");
+ }
+ Assert.assertEquals("Service resumed without permission",
+ PackageManager.PERMISSION_GRANTED, mContext.getPackageManager()
+ .checkPermission(ACCESS_FINE_LOCATION, APP_PKG_NAME));
+ }));
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 237405974L)
+ public void testCustomPermissionIsGrantedOneTime() throws Throwable {
+ startApp(new ComponentName(CUSTOM_CAMERA_PERM_APP_PKG_NAME,
+ CUSTOM_CAMERA_PERM_APP_PKG_NAME + ".RequestCameraPermission"));
+
+ // We're only manually granting CAMERA, but the app will later request CUSTOM and get it
+ // granted silently. This is intentional since it's in the same group but both should
+ // eventually be revoked
+ clickOneTimeButton();
+
+ // Just waiting for the revocation
+ eventually(() -> Assert.assertEquals(PackageManager.PERMISSION_DENIED,
+ mContext.getPackageManager()
+ .checkPermission(CAMERA, CUSTOM_CAMERA_PERM_APP_PKG_NAME)), 30000);
+
+ // This checks the vulnerability
+ eventually(() -> Assert.assertEquals(PackageManager.PERMISSION_DENIED,
+ mContext.getPackageManager()
+ .checkPermission(CUSTOM_PERMISSION, CUSTOM_CAMERA_PERM_APP_PKG_NAME)),
+ 30000);
+
+ }
+
+ private void assertGrantedState(String s, int permissionGranted, long timeoutMillis) {
+ eventually(() -> Assert.assertEquals(s,
+ permissionGranted, mPackageManager
+ .checkPermission(ACCESS_FINE_LOCATION, APP_PKG_NAME)), timeoutMillis);
+ }
+
+ private void assertGranted(long timeoutMillis) {
+ assertGrantedState("Permission was never granted", PackageManager.PERMISSION_GRANTED,
+ timeoutMillis);
+ }
+
+ private void assertDenied(long timeoutMillis) {
+ assertGrantedState("Permission was never revoked", PackageManager.PERMISSION_DENIED,
+ timeoutMillis);
+ }
+
+ private void assertExpectedLifespan(CompletableFuture<Long> exitTime, long expectedLifespan)
+ throws InterruptedException, java.util.concurrent.ExecutionException,
+ java.util.concurrent.TimeoutException {
+ long grantedLength = System.currentTimeMillis() - exitTime.get(0, TimeUnit.MILLISECONDS);
+ if (grantedLength + ONE_TIME_TIMER_LOWER_GRACE_PERIOD < expectedLifespan) {
+ throw new AssertionError(
+ "The one time permission lived shorter than expected. expected: "
+ + expectedLifespan + "ms but was: " + grantedLength + "ms");
+ }
+ }
+
+ private void exitApp() {
+ boolean[] hasExited = {false};
+ try {
+ new Thread(() -> {
+ while (!hasExited[0]) {
+ DreamManager mDreamManager = mContext.getSystemService(DreamManager.class);
+ mUiDevice.pressHome();
+ mUiDevice.pressBack();
+ runWithShellPermissionIdentity(() -> {
+ if (mDreamManager.isDreaming()) {
+ mDreamManager.stopDream();
+ }
+ });
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }).start();
+ eventually(() -> {
+ runWithShellPermissionIdentity(() -> {
+ if (mActivityManager.getPackageImportance(APP_PKG_NAME)
+ <= IMPORTANCE_FOREGROUND) {
+ throw new AssertionError("Unable to exit application");
+ }
+ });
+ });
+ } finally {
+ hasExited[0] = true;
+ }
+ }
+
+ private void clickOneTimeButton() throws Throwable {
+ final UiObject2 uiObject = UiAutomatorUtils2.waitFindObject(By.res(
+ "com.android.permissioncontroller:id/permission_allow_one_time_button"), 10000);
+ Thread.sleep(500);
+ uiObject.click();
+ }
+
+ /**
+ * Start the app. The app will request the permissions.
+ */
+ private void startApp(ComponentName componentName) {
+ // One time permission is not applicable for Wear OS.
+ // The only permissions available are Allow or Deny
+ assumeFalse(
+ "Skipping test: One time permission is not supported in Wear OS",
+ FeatureUtil.isWatch());
+ Intent startApp = new Intent();
+ startApp.setComponent(componentName);
+ startApp.setFlags(FLAG_ACTIVITY_NEW_TASK);
+
+ mContext.startActivity(startApp);
+ }
+
+ /**
+ * Start the default app for these tests. The app will request the permissions.
+ */
+ private void startApp() {
+ startApp(new ComponentName(APP_PKG_NAME, APP_PKG_NAME + ".RequestPermission"));
+ }
+
+ private void startAppForegroundService(long lifespanMillis, boolean sticky) {
+ Intent intent = new Intent()
+ .setComponent(new ComponentName(
+ APP_PKG_NAME, APP_PKG_NAME + ".KeepAliveForegroundService"))
+ .putExtra(EXTRA_FOREGROUND_SERVICE_LIFESPAN, lifespanMillis)
+ .putExtra(EXTRA_FOREGROUND_SERVICE_STICKY, sticky);
+ mContext.startService(intent);
+ }
+
+ private CompletableFuture<Long> registerAppExitListener() {
+ CompletableFuture<Long> exitTimeCallback = new CompletableFuture<>();
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(APP_PKG_NAME, 0);
+ runWithShellPermissionIdentity(() ->
+ mActivityManager.addOnUidImportanceListener(new SingleAppExitListener(
+ uid, IMPORTANCE_FOREGROUND, exitTimeCallback), IMPORTANCE_FOREGROUND));
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new AssertionError("Package not found.", e);
+ }
+ return exitTimeCallback;
+ }
+
+ private class SingleAppExitListener implements ActivityManager.OnUidImportanceListener {
+
+ private final int mUid;
+ private final int mImportance;
+ private final CompletableFuture<Long> mCallback;
+
+ SingleAppExitListener(int uid, int importance, CompletableFuture<Long> callback) {
+ mUid = uid;
+ mImportance = importance;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mUid && importance > mImportance) {
+ mCallback.complete(System.currentTimeMillis());
+ mActivityManager.removeOnUidImportanceListener(this);
+ }
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PackageManagerRequiringPermissionsTest.java b/tests/cts/permission/src/android/permission/cts/PackageManagerRequiringPermissionsTest.java
new file mode 100644
index 000000000..7ebb09f98
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PackageManagerRequiringPermissionsTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.AppModeFull;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify the PackageManager related operations require specific permissions.
+ */
+@SmallTest
+public class PackageManagerRequiringPermissionsTest extends AndroidTestCase {
+ // Must be a known-present application package other than the one hosting this class
+ private static final String PACKAGE_NAME = "android";
+
+ private PackageManager mPackageManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPackageManager = getContext().getPackageManager();
+ assertNotNull(mPackageManager);
+ }
+
+ /**
+ * Verify that PackageManager.setApplicationEnabledSetting requires permission.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_COMPONENT_ENABLED_STATE}.
+ */
+ public void testSetApplicationEnabledSetting() {
+ try {
+ mPackageManager.setApplicationEnabledSetting(PACKAGE_NAME,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ fail("PackageManager.setApplicationEnabledSetting did not throw SecurityException as"
+ + "expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that PackageManager.addPreferredActivity requires permission.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SET_PREFERRED_APPLICATIONS}.
+ */
+ public void testAddPreferredActivity() {
+ try {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
+ filter.addCategory(Intent.CATEGORY_HOME);
+ mPackageManager.addPreferredActivity(filter, 0, null, null);
+ fail("PackageManager.addPreferredActivity did not throw" +
+ " SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that PackageManager.clearPackagePreferredActivities requires permission.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#SET_PREFERRED_APPLICATIONS}.
+ */
+ @AppModeFull(reason = "clearPackagePreferredActivities always returns null for instant apps "
+ + "(it does not even check for permissions)")
+ public void testClearPackagePreferredActivities() {
+ try {
+ mPackageManager.clearPackagePreferredActivities(null);
+ fail("PackageManager.clearPackagePreferredActivities did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that PackageManager.verifyPendingInstall requires permission.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#PACKAGE_VERIFICATION_AGENT}
+ */
+ public void testVerifyPendingInstall() {
+ try {
+ mPackageManager.verifyPendingInstall(1, 1);
+ fail("PackageManager.verifyPendingInstall did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that PackageManager.extendVerificationTimeout requires permission.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#PACKAGE_VERIFICATION_AGENT}.
+ */
+ public void testExtendVerificationTimeout() {
+ try {
+ mPackageManager.extendVerificationTimeout(1, 1, 10000);
+ fail("PackageManager.extendVerificationTimeout did not throw SecurityException"
+ + " as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionControllerTest.java b/tests/cts/permission/src/android/permission/cts/PermissionControllerTest.java
new file mode 100644
index 000000000..4367d2bf6
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionControllerTest.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.BODY_SENSORS;
+import static android.Manifest.permission.READ_CALENDAR;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.WRITE_CALENDAR;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.permissionToOp;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED;
+import static android.permission.PermissionControllerManager.REASON_INSTALLER_POLICY_VIOLATION;
+import static android.permission.PermissionControllerManager.REASON_MALWARE;
+import static android.permission.cts.PermissionUtils.grantPermission;
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.isPermissionGranted;
+
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.Collections.singletonList;
+
+import android.app.AppOpsManager;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PermissionGroupInfo;
+import android.permission.PermissionControllerManager;
+import android.permission.RuntimePermissionPresentationInfo;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Test {@link PermissionControllerManager}
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot talk to permission controller")
+public class PermissionControllerTest {
+ private static final String APK =
+ "/data/local/tmp/cts-permission/CtsAppThatAccessesLocationOnCommand.apk";
+ private static final String APP = "android.permission.cts.appthataccesseslocation";
+ private static final String APK2 =
+ "/data/local/tmp/cts-permission/"
+ + "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk";
+ private static final String APP2 = "android.permission.cts.appthatrequestcustompermission";
+ private static final String CUSTOM_PERMISSION =
+ "android.permission.cts.appthatrequestcustompermission.TEST_PERMISSION";
+
+ private static final UiAutomation sUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final PermissionControllerManager sController =
+ sContext.getSystemService(PermissionControllerManager.class);
+
+ @Before
+ @After
+ public void resetAppState() {
+ runWithShellPermissionIdentity(() -> {
+ sUiAutomation.grantRuntimePermission(APP, ACCESS_FINE_LOCATION);
+ sUiAutomation.grantRuntimePermission(APP, ACCESS_BACKGROUND_LOCATION);
+ setAppOp(APP, ACCESS_FINE_LOCATION, MODE_ALLOWED);
+ });
+ }
+
+ @BeforeClass
+ public static void installApp() {
+ runShellCommandOrThrow("pm install -r -g " + APK);
+ runShellCommandOrThrow("pm install -r " + APK2);
+ }
+
+ @AfterClass
+ public static void uninstallApp() {
+ runShellCommand("pm uninstall " + APP);
+ runShellCommand("pm uninstall " + APP2);
+ }
+
+ private @NonNull Map<String, List<String>> revokePermissions(
+ @NonNull Map<String, List<String>> request, boolean doDryRun, int reason,
+ @NonNull Executor executor) throws Exception {
+ AtomicReference<Map<String, List<String>>> result = new AtomicReference<>();
+
+ sController.revokeRuntimePermissions(request, doDryRun, reason, executor,
+ new PermissionControllerManager.OnRevokeRuntimePermissionsCallback() {
+ @Override
+ public void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> r) {
+ synchronized (result) {
+ result.set(r);
+ result.notifyAll();
+ }
+ }
+ });
+
+ synchronized (result) {
+ while (result.get() == null) {
+ result.wait();
+ }
+ }
+
+ return result.get();
+ }
+
+ private @NonNull Map<String, List<String>> revokePermissions(
+ @NonNull Map<String, List<String>> request, boolean doDryRun, boolean adoptShell)
+ throws Exception {
+ if (adoptShell) {
+ Map<String, List<String>> revokeRet =
+ callWithShellPermissionIdentity(() -> revokePermissions(
+ request, doDryRun, REASON_MALWARE, sContext.getMainExecutor()));
+ return revokeRet;
+ }
+ return revokePermissions(request, doDryRun, REASON_MALWARE, sContext.getMainExecutor());
+ }
+
+ private @NonNull Map<String, List<String>> revokePermissions(
+ @NonNull Map<String, List<String>> request, boolean doDryRun) throws Exception {
+ return revokePermissions(request, doDryRun, true);
+ }
+
+ private void setAppOp(@NonNull String pkg, @NonNull String perm, int mode) throws Exception {
+ sContext.getSystemService(AppOpsManager.class).setUidMode(permissionToOp(perm),
+ sContext.getPackageManager().getPackageUid(pkg, 0), mode);
+ }
+
+ private Map<String, List<String>> buildRevokeRequest(@NonNull String app,
+ @NonNull String permission) {
+ return Collections.singletonMap(app, singletonList(permission));
+ }
+
+ private void assertRuntimePermissionLabelsAreValid(List<String> runtimePermissions,
+ List<RuntimePermissionPresentationInfo> permissionInfos, int expectedRuntimeGranted,
+ String app) throws Exception {
+ int numRuntimeGranted = 0;
+ for (String permission : runtimePermissions) {
+ if (isPermissionGranted(app, permission)) {
+ numRuntimeGranted++;
+ }
+ }
+ assertThat(numRuntimeGranted).isEqualTo(expectedRuntimeGranted);
+
+ ArrayList<CharSequence> maybeStandardPermissionLabels = new ArrayList<>();
+ ArrayList<CharSequence> nonStandardPermissionLabels = new ArrayList<>();
+ for (PermissionGroupInfo permGroup : sContext.getPackageManager().getAllPermissionGroups(
+ 0)) {
+ CharSequence permissionGroupLabel = permGroup.loadLabel(sContext.getPackageManager());
+ if (permGroup.packageName.equals("android")) {
+ maybeStandardPermissionLabels.add(permissionGroupLabel);
+ } else {
+ nonStandardPermissionLabels.add(permissionGroupLabel);
+ }
+ }
+
+ int numInfosGranted = 0;
+
+ for (RuntimePermissionPresentationInfo permissionInfo : permissionInfos) {
+ CharSequence permissionGroupLabel = permissionInfo.getLabel();
+
+ // PermissionInfo should be included in exactly one of existing (possibly) standard
+ // or nonstandard permission groups
+ if (permissionInfo.isStandard()) {
+ assertThat(maybeStandardPermissionLabels).contains(permissionGroupLabel);
+ } else {
+ assertThat(nonStandardPermissionLabels).contains(permissionGroupLabel);
+ }
+ if (permissionInfo.isGranted()) {
+ numInfosGranted++;
+ }
+ }
+
+ // Each permissionInfo represents one or more runtime permissions, but we don't have a
+ // mapping, so we check that we have at least as many runtimePermissions as permissionInfos
+ assertThat(numRuntimeGranted).isAtLeast(numInfosGranted);
+ }
+
+ @Test
+ public void revokePermissionsDryRunSinglePermission() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+
+ Map<String, List<String>> result = revokePermissions(request, true);
+
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.get(APP)).isNotNull();
+ assertThat(result.get(APP)).containsExactly(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ @Test
+ public void revokePermissionsSinglePermission() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+
+ revokePermissions(request, false);
+
+ assertThat(sContext.getPackageManager().checkPermission(ACCESS_BACKGROUND_LOCATION,
+ APP)).isEqualTo(PERMISSION_DENIED);
+ }
+
+ @Test
+ public void revokePermissionsDoNotAlreadyRevokedPermission() throws Exception {
+ // Properly revoke the permission
+ runWithShellPermissionIdentity(() -> {
+ sUiAutomation.revokeRuntimePermission(APP, ACCESS_BACKGROUND_LOCATION);
+ setAppOp(APP, ACCESS_FINE_LOCATION, MODE_FOREGROUND);
+ });
+
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+ Map<String, List<String>> result = revokePermissions(request, false);
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void revokePermissionsDryRunForegroundPermission() throws Exception {
+ assertThat(sContext.getPackageManager().checkPermission(ACCESS_FINE_LOCATION,
+ APP)).isEqualTo(PERMISSION_GRANTED);
+
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_FINE_LOCATION);
+ Map<String, List<String>> result = revokePermissions(request, true);
+
+ assertThat(result.size()).isEqualTo(1);
+ assertThat(result.get(APP)).isNotNull();
+ assertThat(result.get(APP)).containsExactly(ACCESS_FINE_LOCATION,
+ ACCESS_BACKGROUND_LOCATION, ACCESS_COARSE_LOCATION);
+ }
+
+ @Test
+ public void revokePermissionsUnrequestedPermission() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, READ_CONTACTS);
+
+ Map<String, List<String>> result = revokePermissions(request, false);
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void revokeFromUnknownPackage() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest("invalid.app", READ_CONTACTS);
+
+ Map<String, List<String>> result = revokePermissions(request, false);
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void revokePermissionsFromUnknownPermission() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, "unknown.permission");
+
+ Map<String, List<String>> result = revokePermissions(request, false);
+
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void revokePermissionsPolicyViolationFromWrongPackage() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_FINE_LOCATION);
+ Map<String, List<String>> result = callWithShellPermissionIdentity(
+ () -> revokePermissions(request,
+ false, REASON_INSTALLER_POLICY_VIOLATION, sContext.getMainExecutor()));
+ assertThat(result).isEmpty();
+ }
+
+ @Test
+ public void revokePermissionsWithExecutorForCallback() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+
+ AtomicBoolean wasRunOnExecutor = new AtomicBoolean();
+ runWithShellPermissionIdentity(() ->
+ revokePermissions(request, true, REASON_MALWARE, command -> {
+ wasRunOnExecutor.set(true);
+ command.run();
+ }));
+
+ assertThat(wasRunOnExecutor.get()).isTrue();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionsWithNullPkg() throws Exception {
+ Map<String, List<String>> request = Collections.singletonMap(null,
+ singletonList(ACCESS_FINE_LOCATION));
+
+ revokePermissions(request, true);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionsWithNullPermissions() throws Exception {
+ Map<String, List<String>> request = Collections.singletonMap(APP, null);
+
+ revokePermissions(request, true);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionsWithNullPermission() throws Exception {
+ Map<String, List<String>> request = Collections.singletonMap(APP,
+ singletonList(null));
+
+ revokePermissions(request, true);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionsWithNullRequests() {
+ sController.revokeRuntimePermissions(null, false, REASON_MALWARE,
+ sContext.getMainExecutor(),
+ new PermissionControllerManager.OnRevokeRuntimePermissionsCallback() {
+ @Override
+ public void onRevokeRuntimePermissions(
+ @NonNull Map<String, List<String>> revoked) {
+ }
+ });
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionsWithNullCallback() {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+
+ sController.revokeRuntimePermissions(request, false, REASON_MALWARE,
+ sContext.getMainExecutor(), null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionsWithNullExecutor() {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+
+ sController.revokeRuntimePermissions(request, false, REASON_MALWARE, null,
+ new PermissionControllerManager.OnRevokeRuntimePermissionsCallback() {
+ @Override
+ public void onRevokeRuntimePermissions(
+ @NonNull Map<String, List<String>> revoked) {
+
+ }
+ });
+ }
+
+ @Test(expected = SecurityException.class)
+ public void revokePermissionsWithoutPermission() throws Exception {
+ Map<String, List<String>> request = buildRevokeRequest(APP, ACCESS_BACKGROUND_LOCATION);
+
+ // This will fail as the test-app does not have the required permission
+ revokePermissions(request, true, false);
+ }
+
+ @Test
+ public void getAppPermissionsForApp() throws Exception {
+ CompletableFuture<List<RuntimePermissionPresentationInfo>> futurePermissionInfos =
+ new CompletableFuture<>();
+
+ List<String> runtimePermissions;
+ List<RuntimePermissionPresentationInfo> permissionInfos;
+
+ sUiAutomation.adoptShellPermissionIdentity();
+ try {
+ sController.getAppPermissions(APP, futurePermissionInfos::complete, null);
+ runtimePermissions = PermissionUtils.getRuntimePermissions(APP);
+ assertThat(runtimePermissions).isNotEmpty();
+ permissionInfos = futurePermissionInfos.get();
+ } finally {
+ sUiAutomation.dropShellPermissionIdentity();
+ }
+
+ assertRuntimePermissionLabelsAreValid(runtimePermissions, permissionInfos, 3, APP);
+ }
+
+ @Test
+ public void getAppPermissionsForCustomApp() throws Exception {
+ CompletableFuture<List<RuntimePermissionPresentationInfo>> futurePermissionInfos =
+ new CompletableFuture<>();
+
+ // Grant all requested permissions except READ_CALENDAR
+ sUiAutomation.grantRuntimePermission(APP2, CUSTOM_PERMISSION);
+ PermissionUtils.grantPermission(APP2, BODY_SENSORS);
+ PermissionUtils.grantPermission(APP2, READ_CONTACTS);
+ PermissionUtils.grantPermission(APP2, WRITE_CALENDAR);
+
+ List<String> runtimePermissions;
+ List<RuntimePermissionPresentationInfo> permissionInfos;
+ sUiAutomation.adoptShellPermissionIdentity();
+ try {
+ sController.getAppPermissions(APP2, futurePermissionInfos::complete, null);
+ runtimePermissions = PermissionUtils.getRuntimePermissions(APP2);
+
+ permissionInfos = futurePermissionInfos.get();
+ } finally {
+ sUiAutomation.dropShellPermissionIdentity();
+ }
+
+ assertThat(permissionInfos).isNotEmpty();
+ assertThat(runtimePermissions.size()).isEqualTo(6);
+ assertRuntimePermissionLabelsAreValid(runtimePermissions, permissionInfos, 4, APP2);
+ }
+
+ @Test
+ public void revokePermissionAutomaticallyExtendsToWholeGroup() throws Exception {
+ grantPermission(APP2, READ_CALENDAR);
+ grantPermission(APP2, WRITE_CALENDAR);
+
+ runWithShellPermissionIdentity(
+ () -> {
+ sController.revokeRuntimePermission(APP2, READ_CALENDAR);
+
+ eventually(() -> {
+ assertThat(isGranted(APP2, READ_CALENDAR)).isEqualTo(false);
+ // revokePermission automatically extends the revocation to whole group
+ assertThat(isGranted(APP2, WRITE_CALENDAR)).isEqualTo(false);
+ });
+ });
+ }
+
+ @Test
+ public void revokePermissionCustom() throws Exception {
+ sUiAutomation.grantRuntimePermission(APP2, CUSTOM_PERMISSION);
+
+ runWithShellPermissionIdentity(
+ () -> {
+ sController.revokeRuntimePermission(APP2, CUSTOM_PERMISSION);
+
+ eventually(() -> {
+ assertThat(isPermissionGranted(APP2, CUSTOM_PERMISSION)).isEqualTo(false);
+ });
+ });
+ }
+
+ @Test
+ public void revokePermissionWithInvalidPkg() throws Exception {
+ // No return value, call is ignored
+ runWithShellPermissionIdentity(
+ () -> sController.revokeRuntimePermission("invalid.package", READ_CALENDAR));
+ }
+
+ @Test
+ public void revokePermissionWithInvalidPermission() throws Exception {
+ // No return value, call is ignored
+ runWithShellPermissionIdentity(
+ () -> sController.revokeRuntimePermission(APP2, "invalid.permission"));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionWithNullPkg() throws Exception {
+ sController.revokeRuntimePermission(null, READ_CALENDAR);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void revokePermissionWithNullPermission() throws Exception {
+ sController.revokeRuntimePermission(APP2, null);
+ }
+
+ // TODO: Add more tests for countPermissionAppsGranted when the method can be safely called
+ // multiple times in a row
+
+ @Test
+ public void countPermissionAppsGranted() {
+ runWithShellPermissionIdentity(
+ () -> {
+ CompletableFuture<Integer> numApps = new CompletableFuture<>();
+
+ sController.countPermissionApps(singletonList(ACCESS_FINE_LOCATION),
+ COUNT_ONLY_WHEN_GRANTED, numApps::complete, null);
+
+ // TODO: Better would be to count before, grant a permission, count again and
+ // then compare before and after
+ assertThat(numApps.get()).isAtLeast(1);
+ });
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void countPermissionAppsNullPermission() {
+ sController.countPermissionApps(null, 0, (n) -> { }, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void countPermissionAppsInvalidFlags() {
+ sController.countPermissionApps(singletonList(ACCESS_FINE_LOCATION), -1, (n) -> { }, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void countPermissionAppsNullCallback() {
+ sController.countPermissionApps(singletonList(ACCESS_FINE_LOCATION), 0, null, null);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java b/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java
new file mode 100644
index 000000000..eb0212cef
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionFlagsTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
+import static android.Manifest.permission.READ_CALL_LOG;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.permission.cts.PermissionUtils.clearAppState;
+import static android.permission.cts.PermissionUtils.getAllPermissionFlags;
+import static android.permission.cts.PermissionUtils.getPermissionFlags;
+import static android.permission.cts.PermissionUtils.install;
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.setPermissionFlags;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+import android.platform.test.annotations.PlatinumTest;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests how permission flags behave.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Cannot read permission flags of other app.")
+@PlatinumTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class PermissionFlagsTest {
+ /** The package name of most apps used in the test */
+ private static final String APP_PKG = "android.permission.cts.appthatrequestpermission";
+ private static final String APP_SYSTEM_ALERT_WINDOW_PKG =
+ "android.permission.cts.usesystemalertwindowpermission";
+
+ private static final String TMP_DIR = "/data/local/tmp/cts-permission/";
+ private static final String APK_CONTACTS_15 =
+ TMP_DIR + "CtsAppThatRequestsContactsPermission15.apk";
+ private static final String APK_LOCATION_22 =
+ TMP_DIR + "CtsAppThatRequestsLocationPermission22.apk";
+ private static final String APK_LOCATION_28 =
+ TMP_DIR + "CtsAppThatRequestsLocationPermission28.apk";
+ private static final String APK_STORAGE_22 =
+ TMP_DIR + "CtsAppThatRequestsStoragePermission22.apk";
+ private static final String APK_SYSTEM_ALERT_WINDOW_23 =
+ TMP_DIR + "CtsAppThatRequestsSystemAlertWindow23.apk";
+
+ @After
+ @Before
+ public void uninstallTestApp() {
+ uninstallApp(APP_PKG);
+ uninstallApp(APP_SYSTEM_ALERT_WINDOW_PKG);
+ }
+
+ @Test
+ public void implicitPermission() {
+ install(APK_LOCATION_28);
+
+ assertEquals(FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION));
+ }
+
+ @Test
+ public void implicitPermissionPreM() throws Exception {
+ install(APK_STORAGE_22);
+
+ // Test ACCESS_MEDIA_LOCATION which is split from READ_EXTERNAL_STORAGE but won't get
+ // REVOKE_ON_UPGRADE, while it should still get REVIEW_REQUIRED when pre-M.
+ assertEquals(FLAG_PERMISSION_REVIEW_REQUIRED, getPermissionFlags(APP_PKG,
+ ACCESS_MEDIA_LOCATION) & FLAG_PERMISSION_REVIEW_REQUIRED);
+ }
+
+ @Test
+ public void regularPermission() {
+ install(APK_LOCATION_28);
+
+ assertEquals(0, getPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION));
+ }
+
+ @Test
+ public void regularPermissionPreM() {
+ install(APK_CONTACTS_15);
+
+ assertEquals(FLAG_PERMISSION_REVIEW_REQUIRED,
+ getPermissionFlags(APP_PKG, READ_CONTACTS) & FLAG_PERMISSION_REVIEW_REQUIRED);
+ }
+
+ @Test
+ public void clearRegularPermissionPreM() {
+ install(APK_CONTACTS_15);
+
+ int defaultState = getPermissionFlags(APP_PKG, READ_CONTACTS);
+ setPermissionFlags(APP_PKG, READ_CONTACTS, FLAG_PERMISSION_REVIEW_REQUIRED, 0);
+ setPermissionFlags(APP_PKG, READ_CONTACTS,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+
+ clearAppState(APP_PKG);
+
+ eventually(() -> assertEquals(defaultState, getPermissionFlags(APP_PKG, READ_CONTACTS)));
+ }
+
+ @Test
+ public void clearImplicitPermissionPreM() {
+ install(APK_CONTACTS_15);
+
+ int defaultState = getPermissionFlags(APP_PKG, READ_CALL_LOG);
+ setPermissionFlags(APP_PKG, READ_CALL_LOG, FLAG_PERMISSION_REVIEW_REQUIRED, 0);
+ setPermissionFlags(APP_PKG, READ_CALL_LOG,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+
+ clearAppState(APP_PKG);
+
+ eventually(() -> assertEquals(defaultState, getPermissionFlags(APP_PKG, READ_CALL_LOG)));
+ }
+
+ @Test
+ public void clearRegularPermission() {
+ install(APK_LOCATION_28);
+
+ int defaultState = getPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION);
+ setPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+
+ clearAppState(APP_PKG);
+
+ eventually(() -> assertEquals(defaultState,
+ getPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION)));
+ }
+
+ @Test
+ public void clearImplicitPermission() {
+ install(APK_LOCATION_28);
+
+ int defaultState = getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+ setPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+
+ clearAppState(APP_PKG);
+
+ eventually(() -> assertEquals(defaultState,
+ getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION)));
+ }
+
+ @Test
+ public void reinstallPreM() {
+ install(APK_CONTACTS_15);
+ install(APK_CONTACTS_15);
+
+ assertEquals(FLAG_PERMISSION_REVIEW_REQUIRED,
+ getPermissionFlags(APP_PKG, READ_CONTACTS) & FLAG_PERMISSION_REVIEW_REQUIRED);
+ }
+
+ @Test
+ public void reinstallDoesNotOverrideChangesPreM() {
+ install(APK_CONTACTS_15);
+
+ setPermissionFlags(APP_PKG, READ_CONTACTS, FLAG_PERMISSION_REVIEW_REQUIRED, 0);
+ setPermissionFlags(APP_PKG, READ_CONTACTS,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+
+ install(APK_CONTACTS_15);
+
+ assertEquals(FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ getPermissionFlags(APP_PKG, READ_CONTACTS) & (FLAG_PERMISSION_USER_SET
+ | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_REVIEW_REQUIRED));
+ }
+
+ @Test
+ public void reinstall() {
+ install(APK_LOCATION_28);
+ install(APK_LOCATION_28);
+
+ assertEquals(0, getPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION));
+ assertEquals(FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION));
+ }
+
+ @Test
+ public void reinstallDoesNotOverrideChanges() {
+ install(APK_LOCATION_28);
+
+ setPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+ setPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED);
+
+ install(APK_LOCATION_28);
+
+ assertEquals(FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED,
+ getPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION));
+
+ assertEquals(FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED
+ | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION));
+ }
+
+ @Test
+ public void revokeOnUpgrade() throws Exception {
+ install(APK_LOCATION_22);
+
+ install(APK_LOCATION_28);
+
+ assertFalse(isGranted(APP_PKG, ACCESS_COARSE_LOCATION));
+ assertFalse(isGranted(APP_PKG, ACCESS_BACKGROUND_LOCATION));
+ assertEquals(0,getPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION)
+ & FLAG_PERMISSION_REVOKED_COMPAT);
+ assertEquals(0,getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION)
+ & FLAG_PERMISSION_REVOKED_COMPAT);
+ }
+
+ @AsbSecurityTest(cveBugId = 283006437)
+ @Test
+ public void nonRuntimePermissionFlagsPreservedAfterReinstall() throws Exception {
+ install(APK_SYSTEM_ALERT_WINDOW_23);
+
+ int flags = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_GRANTED_BY_ROLE;
+ setPermissionFlags(APP_SYSTEM_ALERT_WINDOW_PKG, SYSTEM_ALERT_WINDOW, flags, flags);
+ assertEquals(flags, getAllPermissionFlags(APP_SYSTEM_ALERT_WINDOW_PKG, SYSTEM_ALERT_WINDOW)
+ & flags);
+
+ install(APK_SYSTEM_ALERT_WINDOW_23);
+
+ assertEquals(flags, getAllPermissionFlags(APP_SYSTEM_ALERT_WINDOW_PKG, SYSTEM_ALERT_WINDOW)
+ & flags);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionGroupChange.java b/tests/cts/permission/src/android/permission/cts/PermissionGroupChange.java
new file mode 100644
index 000000000..46fe167c0
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionGroupChange.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+import android.widget.ScrollView;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class PermissionGroupChange {
+ private static final String APP_PKG_NAME = "android.permission.cts.appthatrequestpermission";
+ private static final long EXPECTED_BEHAVIOR_TIMEOUT_SEC = 15;
+ private static final long UNEXPECTED_BEHAVIOR_TIMEOUT_SEC = 2;
+
+ private Context mContext;
+ private UiDevice mUiDevice;
+ private String mAllowButtonText = null;
+
+ @Before
+ public void setContextAndUiDevice() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+
+ @Before
+ public void uninstallAndWakeUpScreen() {
+ runShellCommand("pm uninstall " + APP_PKG_NAME);
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+ }
+
+ /**
+ * Retry for a time until the runnable stops throwing.
+ *
+ * @param runnable The condition to execute
+ * @param timeoutSec The time to try
+ */
+ private void eventually(ThrowingRunnable runnable, long timeoutSec) throws Throwable {
+ long startTime = System.nanoTime();
+ while (true) {
+ try {
+ runnable.run();
+ return;
+ } catch (Throwable t) {
+ if (System.nanoTime() - startTime < TimeUnit.SECONDS.toNanos(timeoutSec)) {
+ Thread.sleep(100);
+ continue;
+ }
+
+ throw t;
+ }
+ }
+ }
+
+
+ private void scrollToBottomIfWatch() throws Exception {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ UiScrollable scrollable = new UiScrollable(
+ new UiSelector().className(ScrollView.class));
+ if (scrollable.exists()) {
+ scrollable.flingToEnd(10);
+ }
+ }
+ }
+
+ protected void clickAllowButton() throws Exception {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (mAllowButtonText == null) {
+ mAllowButtonText = getPermissionControllerString("grant_dialog_button_allow");
+ }
+ mUiDevice.findObject(By.text(Pattern.compile(Pattern.quote(mAllowButtonText),
+ Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE))).click();
+ } else {
+ mUiDevice.findObject(By.res(
+ "com.android.permissioncontroller:id/permission_allow_button")).click();
+ }
+ }
+
+ private void grantPermissionViaUi() throws Throwable {
+ eventually(() -> {
+ scrollToBottomIfWatch();
+ clickAllowButton();
+ }, EXPECTED_BEHAVIOR_TIMEOUT_SEC);
+ }
+
+ private void waitUntilPermissionGranted(String permName, long timeoutSec) throws Throwable {
+ eventually(() -> {
+ PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(APP_PKG_NAME,
+ GET_PERMISSIONS);
+
+ for (int i = 0; i < appInfo.requestedPermissions.length; i++) {
+ if (appInfo.requestedPermissions[i].equals(permName)
+ && ((appInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+ != 0)) {
+ return;
+ }
+ }
+
+ fail(permName + " not granted");
+ }, timeoutSec);
+ }
+
+ private void installApp(String apk) {
+ String installResult = SystemUtil.runShellCommandOrThrow(
+ "pm install -r /data/local/tmp/cts-permission/" + apk + ".apk");
+ assertEquals("Success", installResult.trim());
+ }
+
+ /**
+ * Start the app. The app will request the permissions.
+ */
+ private void startApp() {
+ Intent startApp = new Intent();
+ startApp.setComponent(new ComponentName(APP_PKG_NAME, APP_PKG_NAME + ".RequestPermission"));
+ startApp.setFlags(FLAG_ACTIVITY_NEW_TASK);
+
+ mContext.startActivity(startApp);
+ }
+
+ @After
+ public void uninstallTestApp() {
+ runShellCommand("pm uninstall android.permission.cts.appthatrequestpermission");
+ }
+
+ @Test
+ @AppModeFull
+ @AsbSecurityTest(cveBugId = 72710897)
+ public void permissionGroupShouldNotBeAutoGrantedIfNewMember() throws Throwable {
+ installApp("CtsAppThatRequestsPermissionAandB");
+
+ startApp();
+ grantPermissionViaUi();
+ waitUntilPermissionGranted("android.permission.cts.appthatrequestpermission.A",
+ EXPECTED_BEHAVIOR_TIMEOUT_SEC);
+
+ // Update app which changes the permission group of "android.permission.cts
+ // .appthatrequestpermission.A" to the same as "android.permission.cts.C"
+ installApp("CtsAppThatRequestsPermissionAandC");
+
+ startApp();
+ try {
+ // The permission should not be auto-granted
+ waitUntilPermissionGranted("android.permission.cts.C", UNEXPECTED_BEHAVIOR_TIMEOUT_SEC);
+ fail("android.permission.cts.C was auto-granted");
+ } catch (Throwable expected) {
+ assertEquals("android.permission.cts.C not granted", expected.getMessage());
+ }
+ }
+
+ private String getPermissionControllerString(String res)
+ throws PackageManager.NameNotFoundException {
+ Resources permissionControllerResources = mContext.createPackageContext(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ .getResources();
+ return permissionControllerResources.getString(permissionControllerResources
+ .getIdentifier(res, "string", "com.android.permissioncontroller"));
+ }
+
+ private interface ThrowingRunnable {
+ void run() throws Throwable;
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionManagerNativeJniTest.java b/tests/cts/permission/src/android/permission/cts/PermissionManagerNativeJniTest.java
new file mode 100644
index 000000000..868b3d1fc
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionManagerNativeJniTest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts;
+
+import org.junit.runner.RunWith;
+import com.android.gtestrunner.GtestRunner;
+import com.android.gtestrunner.TargetLibrary;
+
+@RunWith(GtestRunner.class)
+@TargetLibrary("permissionmanager_native_test")
+public class PermissionManagerNativeJniTest {}
+
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionManagerTest.java b/tests/cts/permission/src/android/permission/cts/PermissionManagerTest.java
new file mode 100644
index 000000000..6fa940aa0
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionManagerTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.permission.PermissionManager;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link PermissionManager}
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot talk to permission manager")
+public class PermissionManagerTest {
+ private final Context mContext = InstrumentationRegistry.getTargetContext();
+
+ @Test
+ public void testRuntimePermissionsVersion() throws Exception {
+ final PermissionManager permissionManager =
+ mContext.getSystemService(PermissionManager.class);
+ final int version = callWithShellPermissionIdentity(() ->
+ permissionManager.getRuntimePermissionsVersion());
+ assertThat(version).isAtLeast(0);
+ runWithShellPermissionIdentity(() ->
+ permissionManager.setRuntimePermissionsVersion(version));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionStubActivity.java b/tests/cts/permission/src/android/permission/cts/PermissionStubActivity.java
new file mode 100644
index 000000000..dd611777c
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionStubActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ListView;
+
+/**
+ * A minimal application for Window test.
+ */
+public class PermissionStubActivity extends Activity {
+ private ListView mListView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mListView = new ListView(this);
+ mListView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT));
+
+ setContentView(mListView);
+ }
+
+ public Dialog getDialog() {
+ return new AlertDialog.Builder(PermissionStubActivity.this).create();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java b/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
new file mode 100644
index 000000000..e9e71af20
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeNotNull;
+
+import android.companion.virtual.VirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.OnPermissionsChangedListener;
+import android.permission.flags.Flags;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.virtualdevice.cts.common.FakeAssociationRule;
+
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@AppModeFull(reason = "Instant apps cannot access properties of other apps")
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PermissionUpdateListenerTest {
+ private static final String LOG_TAG = PermissionUpdateListenerTest.class.getSimpleName();
+ private static final String APK =
+ "/data/local/tmp/cts-permission/"
+ + "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk";
+ private static final String PACKAGE_NAME =
+ "android.permission.cts.appthatrequestcustompermission";
+ private static final String PERMISSION_NAME = "android.permission.RECORD_AUDIO";
+ private static final int TIMEOUT = 10000;
+
+ private final Context mDefaultContext =
+ InstrumentationRegistry.getInstrumentation().getContext();
+ private final PackageManager mPackageManager = mDefaultContext.getPackageManager();
+
+ private int mTestAppUid;
+
+ private VirtualDeviceManager mVirtualDeviceManager;
+
+ @Rule
+ public FakeAssociationRule mFakeAssociationRule = new FakeAssociationRule();
+
+ @Rule
+ public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE);
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setup() throws PackageManager.NameNotFoundException, InterruptedException {
+ runShellCommandOrThrow("pm install " + APK);
+ // permission change events are generated for a package install, the wait helps prevent
+ // those permission change events interfere with the test.
+ SystemUtil.waitForBroadcasts();
+ Thread.sleep(1000);
+ mTestAppUid = mPackageManager.getPackageUid(PACKAGE_NAME, 0);
+ mVirtualDeviceManager = mDefaultContext.getSystemService(VirtualDeviceManager.class);
+ }
+
+ @After
+ public void cleanup() {
+ runShellCommand("pm uninstall " + PACKAGE_NAME);
+ }
+
+ @Test
+ public void testGrantPermissionInvokesOldCallback() throws InterruptedException {
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ OnPermissionsChangedListener permissionsChangedListener =
+ uid -> {
+ if (mTestAppUid == uid) {
+ countDownLatch.countDown();
+ }
+ };
+
+ runWithShellPermissionIdentity(() -> {
+ mPackageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ mPackageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
+ });
+ countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+ runWithShellPermissionIdentity(() -> {
+ mPackageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ });
+
+ assertThat(countDownLatch.getCount()).isEqualTo(0);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED})
+ public void testVirtualDeviceGrantPermissionNotifyListener() throws InterruptedException {
+ assumeNotNull(mVirtualDeviceManager);
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceManager.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ new VirtualDeviceParams.Builder().build());
+ Context deviceContext = mDefaultContext.createDeviceContext(virtualDevice.getDeviceId());
+ testGrantPermissionNotifyListener(deviceContext, virtualDevice.getPersistentDeviceId());
+ }
+
+ @Test
+ public void testDefaultDeviceGrantPermissionNotifyListener() throws InterruptedException {
+ testGrantPermissionNotifyListener(
+ mDefaultContext, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ private void testGrantPermissionNotifyListener(
+ Context context, String expectedDeviceId) throws InterruptedException {
+ final PackageManager packageManager = context.getPackageManager();
+ TestOnPermissionsChangedListener permissionsChangedListener =
+ new TestOnPermissionsChangedListener(1);
+ runWithShellPermissionIdentity(() -> {
+ packageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
+ });
+
+ permissionsChangedListener.waitForPermissionChangedCallbacks();
+ runWithShellPermissionIdentity(() -> {
+ packageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ });
+
+ String deviceId = permissionsChangedListener.getNotifiedDeviceId(mTestAppUid);
+ assertThat(deviceId).isEqualTo(expectedDeviceId);
+ }
+
+ @Test
+ public void testDefaultDeviceRevokePermissionNotifyListener() throws InterruptedException {
+ testRevokePermissionNotifyListener(
+ mDefaultContext, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED})
+ public void testVirtualDeviceRevokePermissionNotifyListener() throws InterruptedException {
+ assumeNotNull(mVirtualDeviceManager);
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceManager.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ new VirtualDeviceParams.Builder().build());
+ Context deviceContext = mDefaultContext.createDeviceContext(virtualDevice.getDeviceId());
+ testRevokePermissionNotifyListener(
+ deviceContext, virtualDevice.getPersistentDeviceId());
+ }
+
+ private void testRevokePermissionNotifyListener(
+ Context context, String expectedDeviceId) throws InterruptedException {
+ final PackageManager packageManager = context.getPackageManager();
+ TestOnPermissionsChangedListener permissionsChangedListener =
+ new TestOnPermissionsChangedListener(1);
+ runWithShellPermissionIdentity(() -> {
+ packageManager.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
+ packageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ packageManager.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME,
+ mDefaultContext.getUser());
+ });
+ permissionsChangedListener.waitForPermissionChangedCallbacks();
+ runWithShellPermissionIdentity(() -> {
+ packageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ });
+
+ String deviceId = permissionsChangedListener.getNotifiedDeviceId(mTestAppUid);
+ assertThat(deviceId).isEqualTo(expectedDeviceId);
+ }
+
+ @Test
+ public void testDefaultDeviceUpdatePermissionFlagsNotifyListener() throws InterruptedException {
+ testUpdatePermissionFlagsNotifyListener(
+ mDefaultContext, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Test
+ @RequiresFlagsEnabled({Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED})
+ public void testVirtualDeviceUpdatePermissionFlagsNotifyListener() throws InterruptedException {
+ assumeNotNull(mVirtualDeviceManager);
+ VirtualDeviceManager.VirtualDevice virtualDevice =
+ mVirtualDeviceManager.createVirtualDevice(
+ mFakeAssociationRule.getAssociationInfo().getId(),
+ new VirtualDeviceParams.Builder().build());
+ Context deviceContext = mDefaultContext.createDeviceContext(virtualDevice.getDeviceId());
+ testUpdatePermissionFlagsNotifyListener(
+ deviceContext, virtualDevice.getPersistentDeviceId());
+ }
+
+ private void testUpdatePermissionFlagsNotifyListener(
+ Context context, String expectedDeviceId) throws InterruptedException {
+ TestOnPermissionsChangedListener permissionsChangedListener =
+ new TestOnPermissionsChangedListener(1);
+ final PackageManager packageManager = context.getPackageManager();
+ runWithShellPermissionIdentity(() -> {
+ packageManager.addOnPermissionsChangeListener(permissionsChangedListener);
+ int flag = PackageManager.FLAG_PERMISSION_USER_SET;
+ packageManager.updatePermissionFlags(PERMISSION_NAME, PACKAGE_NAME, flag, flag,
+ mDefaultContext.getUser());
+ });
+ permissionsChangedListener.waitForPermissionChangedCallbacks();
+ runWithShellPermissionIdentity(() -> {
+ packageManager.removeOnPermissionsChangeListener(permissionsChangedListener);
+ });
+
+ String deviceId = permissionsChangedListener.getNotifiedDeviceId(mTestAppUid);
+ assertThat(deviceId).isEqualTo(expectedDeviceId);
+ }
+
+ private class TestOnPermissionsChangedListener
+ implements OnPermissionsChangedListener {
+ // map of uid and persistentDeviceID
+ private final Map<Integer, String> mUidDeviceIdsMap = new ConcurrentHashMap<>();
+ private final CountDownLatch mCountDownLatch;
+
+ TestOnPermissionsChangedListener(int expectedCallbackCount) {
+ mCountDownLatch = new CountDownLatch(expectedCallbackCount);
+ }
+
+ @Override
+ public void onPermissionsChanged(int uid) {
+ // ignored when we implement the new callback.
+ }
+
+ @Override
+ public void onPermissionsChanged(int uid, String deviceId) {
+ if (uid == mTestAppUid) {
+ mCountDownLatch.countDown();
+ mUidDeviceIdsMap.put(uid, deviceId);
+ }
+ }
+
+ String getNotifiedDeviceId(int uid) {
+ return mUidDeviceIdsMap.get(uid);
+ }
+
+ void waitForPermissionChangedCallbacks() throws InterruptedException {
+ mCountDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+ assertThat(mCountDownLatch.getCount()).isEqualTo(0);
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PlatformPermissionGroupMappingTest.kt b/tests/cts/permission/src/android/permission/cts/PlatformPermissionGroupMappingTest.kt
new file mode 100644
index 000000000..2816bbb5a
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PlatformPermissionGroupMappingTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts
+
+import android.os.Build
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+class PlatformPermissionGroupMappingTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context = instrumentation.context
+ private val packageManager = context.packageManager
+
+ @Test
+ fun platformPermissionHasPermissionGroup() {
+ val future = CompletableFuture<String>()
+ packageManager.getGroupOfPlatformPermission(
+ android.Manifest.permission.READ_CALENDAR,
+ context.mainExecutor
+ ) {
+ future.complete(it)
+ }
+ val permissionGroupName = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ assertThat(permissionGroupName).isEqualTo(android.Manifest.permission_group.CALENDAR)
+ }
+
+ @Test
+ fun platformPermissionGroupHasPermission() {
+ val future = CompletableFuture<List<String>>()
+ packageManager.getPlatformPermissionsForGroup(
+ android.Manifest.permission_group.CALENDAR,
+ context.mainExecutor
+ ) {
+ future.complete(it)
+ }
+ val permissionNames = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ assertThat(permissionNames).contains(android.Manifest.permission.READ_CALENDAR)
+ }
+
+ companion object {
+ private const val TIMEOUT_MILLIS = 15 * 1000L
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/PowerManagerServicePermissionTest.java b/tests/cts/permission/src/android/permission/cts/PowerManagerServicePermissionTest.java
new file mode 100644
index 000000000..b842cc0cf
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/PowerManagerServicePermissionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts;
+
+import android.os.PowerManager;
+import android.test.AndroidTestCase;
+
+import java.time.Duration;
+
+public class PowerManagerServicePermissionTest extends AndroidTestCase {
+
+ public void testSetBatterySaver_requiresPermissions() {
+ PowerManager manager = getContext().getSystemService(PowerManager.class);
+ boolean batterySaverOn = manager.isPowerSaveMode();
+
+ try {
+ manager.setPowerSaveModeEnabled(!batterySaverOn);
+ fail("Toggling battery saver requires POWER_SAVER or DEVICE_POWER permission");
+ } catch (SecurityException e) {
+ // Expected Exception
+ }
+ }
+
+ public void testSetDynamicPowerSavings_requiresPermissions() {
+ try {
+ PowerManager manager = getContext().getSystemService(PowerManager.class);
+ manager.setDynamicPowerSaveHint(true, 0);
+ fail("Updating the dynamic power savings state requires the POWER_SAVER permission");
+ } catch (SecurityException e) {
+ // Expected Exception
+ }
+ }
+
+ public void testSetBatteryDischargePrediction_requiresPermissions() {
+ try {
+ PowerManager manager = getContext().getSystemService(PowerManager.class);
+ manager.setBatteryDischargePrediction(Duration.ofMillis(1000), false);
+ fail("Updating the discharge prediction requires the DEVICE_POWER"
+ + " or BATTERY_PREDICTION permission");
+ } catch (SecurityException e) {
+ // Expected Exception
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/ProviderPermissionTest.java b/tests/cts/permission/src/android/permission/cts/ProviderPermissionTest.java
new file mode 100644
index 000000000..83c2ffaee
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ProviderPermissionTest.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+
+import android.app.UiAutomation;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AppModeInstant;
+import android.provider.CallLog;
+import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Tests Permissions related to reading from and writing to providers
+ */
+@MediumTest
+public class ProviderPermissionTest extends AndroidTestCase {
+
+ private static final String TAG = ProviderPermissionTest.class.getSimpleName();
+
+ private static final List<Uri> CONTACT_URIS = List.of(
+ Contacts.People.CONTENT_URI, // Deprecated.
+ ContactsContract.Contacts.CONTENT_FILTER_URI,
+ ContactsContract.Contacts.CONTENT_GROUP_URI,
+ ContactsContract.Contacts.CONTENT_LOOKUP_URI,
+ ContactsContract.CommonDataKinds.Email.CONTENT_URI,
+ ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI,
+ ContactsContract.Directory.CONTENT_URI,
+ ContactsContract.Directory.ENTERPRISE_CONTENT_URI,
+ ContactsContract.Profile.CONTENT_URI);
+
+ /**
+ * Verify that reading contacts requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#READ_CONTACTS}
+ */
+ public void testReadContacts() {
+ for (Uri uri : CONTACT_URIS) {
+ Log.d(TAG, "Checking contacts URI " + uri);
+ assertReadingContentUriRequiresPermission(uri,
+ android.Manifest.permission.READ_CONTACTS);
+ }
+ }
+
+ /**
+ * Verify that writing contacts requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#WRITE_CONTACTS}
+ */
+ public void testWriteContacts() {
+ assertWritingContentUriRequiresPermission(Contacts.People.CONTENT_URI,
+ android.Manifest.permission.WRITE_CONTACTS);
+ }
+
+ /**
+ * Verify that reading call logs requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#READ_CALL_LOG}
+ */
+ @AppModeFull
+ public void testReadCallLog() {
+ assertReadingContentUriRequiresPermission(CallLog.CONTENT_URI,
+ android.Manifest.permission.READ_CALL_LOG);
+ }
+
+ /**
+ * Verify that writing call logs requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#WRITE_CALL_LOG}
+ */
+ @AppModeFull
+ public void testWriteCallLog() {
+ assertWritingContentUriRequiresPermission(CallLog.CONTENT_URI,
+ android.Manifest.permission.WRITE_CALL_LOG);
+ }
+
+ /**
+ * Verify that reading from call-log (a content provider that is not accessible to instant apps)
+ * returns null
+ */
+ @AppModeInstant
+ public void testReadCallLogInstant() {
+ assertNull(getContext().getContentResolver().query(CallLog.CONTENT_URI, null, null, null,
+ null));
+ }
+
+ /**
+ * Verify that writing to call-log (a content provider that is not accessible to instant apps)
+ * yields an IAE.
+ */
+ @AppModeInstant
+ public void testWriteCallLogInstant() {
+ try {
+ getContext().getContentResolver().insert(CallLog.CONTENT_URI, new ContentValues());
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /**
+ * Verify that reading already received SMS messages requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#READ_SMS}
+ *
+ * <p>Note: The WRITE_SMS permission has been removed.
+ */
+ @AppModeFull
+ public void testReadSms() {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ assertReadingContentUriRequiresPermission(Telephony.Sms.CONTENT_URI,
+ android.Manifest.permission.READ_SMS);
+ }
+
+ /**
+ * Verify that reading from 'sms' (a content provider that is not accessible to instant apps)
+ * returns null
+ */
+ @AppModeInstant
+ public void testReadSmsInstant() {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ assertNull(getContext().getContentResolver().query(Telephony.Sms.CONTENT_URI, null, null,
+ null, null));
+ }
+
+ /**
+ * Verify that write to settings requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#WRITE_SETTINGS}
+ */
+ public void testWriteSettings() {
+ final String permission = android.Manifest.permission.WRITE_SETTINGS;
+ ContentValues value = new ContentValues();
+ value.put(Settings.System.NAME, "name");
+ value.put(Settings.System.VALUE, "value_insert");
+
+ try {
+ getContext().getContentResolver().insert(Settings.System.CONTENT_URI, value);
+ fail("expected SecurityException requiring " + permission);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ assertTrue("error message should contain \"" + permission + "\". Got: \""
+ + expected.getMessage() + "\".",
+ expected.getMessage().contains(permission));
+ }
+ }
+
+ /**
+ * Verify that the {@link android.Manifest.permission#MANAGE_DOCUMENTS}
+ * permission is only held by up to one package: whoever handles the
+ * {@link android.content.Intent#ACTION_OPEN_DOCUMENT} intent, if any.
+ * <p>
+ * No other apps should <em>ever</em> attempt to acquire this permission,
+ * since it would give those apps extremely broad access to all storage
+ * providers on the device without user involvement in the arbitration
+ * process. Apps should instead always rely on Uri permission grants for
+ * access, using
+ * {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} and related
+ * APIs.
+ */
+ public void testManageDocuments() {
+ final PackageManager pm = getContext().getPackageManager();
+
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+ final ResolveInfo ri = pm.resolveActivity(intent, 0);
+
+ if (ri != null) {
+ final String validPkg = ri.activityInfo.packageName;
+
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+ android.Manifest.permission.MANAGE_DOCUMENTS
+ }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ for (PackageInfo pi : holding) {
+ if (!Objects.equals(pi.packageName, validPkg)) {
+ fail("Exactly one package (must be " + validPkg
+ + ") can request the MANAGE_DOCUMENTS permission; found package "
+ + pi.packageName + " which must be revoked for security reasons");
+ }
+ }
+ }
+ }
+
+ /**
+ * The {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} permission is
+ * a very powerful permission that grants raw storage access to all devices,
+ * and as such it's only appropriate to be granted to the media stack.
+ * <p>
+ * CDD now requires that all apps requesting this permission also hold the
+ * "Storage" runtime permission, to give users visibility into the
+ * capabilities of each app, and control over those capabilities.
+ * <p>
+ * If the end user revokes the "Storage" permission from an app, but that
+ * app still has raw access to storage via {@code WRITE_MEDIA_STORAGE}, that
+ * would be a CDD violation and a privacy incident.
+ */
+ public void testWriteMediaStorage() throws Exception {
+ final UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ final PackageManager pm = getContext().getPackageManager();
+ final UserHandle userHandle = getContext().getUser();
+ final List<PackageInfo> pkgs = pm.getInstalledPackages(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS);
+ for (PackageInfo pkg : pkgs) {
+ final int appUid = userHandle.getAppId(pkg.applicationInfo.uid);
+ final boolean isSystem = appUid == android.os.Process.SYSTEM_UID;
+ final boolean hasFrontDoor = pm.getLaunchIntentForPackage(pkg.packageName) != null;
+ final boolean grantedMedia = pm.checkPermission(WRITE_MEDIA_STORAGE,
+ pkg.packageName) == PackageManager.PERMISSION_GRANTED;
+
+ if (!isSystem && hasFrontDoor && grantedMedia) {
+ final boolean requestsStorage = contains(pkg.requestedPermissions,
+ MANAGE_EXTERNAL_STORAGE);
+ if (!requestsStorage) {
+ fail("Found " + pkg.packageName + " holding WRITE_MEDIA_STORAGE permission "
+ + "without also requesting MANAGE_EXTERNAL_STORAGE; these permissions "
+ + "must be requested together");
+ }
+
+ final boolean grantedStorage = pm.checkPermission(MANAGE_EXTERNAL_STORAGE,
+ pkg.packageName) == PackageManager.PERMISSION_GRANTED;
+ if (grantedStorage) {
+ final int flags;
+ ui.adoptShellPermissionIdentity("android.permission.GET_RUNTIME_PERMISSIONS");
+ try {
+ flags = pm.getPermissionFlags(MANAGE_EXTERNAL_STORAGE, pkg.packageName,
+ android.os.Process.myUserHandle());
+ } finally {
+ ui.dropShellPermissionIdentity();
+ }
+
+ final boolean isFixed = (flags & (PackageManager.FLAG_PERMISSION_USER_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED
+ | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
+ if (isFixed) {
+ fail("Found " + pkg.packageName + " holding MANAGE_EXTERNAL_STORAGE in a "
+ + "fixed state; this permission must be revokable by the user");
+ }
+ }
+ }
+ }
+ }
+
+ private static boolean contains(String[] haystack, String needle) {
+ if (haystack != null) {
+ for (String test : haystack) {
+ if (Objects.equals(test, needle)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RebootPermissionTest.java b/tests/cts/permission/src/android/permission/cts/RebootPermissionTest.java
new file mode 100644
index 000000000..13f17dce8
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RebootPermissionTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.Intent;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify that rebooting requires Permission.
+ */
+public class RebootPermissionTest extends AndroidTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ /**
+ * Verify that rebooting by sending a broadcast Intent requires Permission.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#REBOOT}.
+ */
+ @SmallTest
+ public void testBroadcastReboot() {
+ try {
+ mContext.sendBroadcast(new Intent(Intent.ACTION_REBOOT));
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RecordSensitiveContentPermissionTest.kt b/tests/cts/permission/src/android/permission/cts/RecordSensitiveContentPermissionTest.kt
new file mode 100644
index 000000000..b80f89938
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RecordSensitiveContentPermissionTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts
+
+import android.os.Build
+import android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SdkSuppress
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+@AppModeFull(reason = "Instant apps cannot install packages")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+class RecordSensitiveContentPermissionTest {
+ @Rule
+ @JvmField
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ fun testRecordSensitiveContentDuringProjection() {
+ val packageManager = InstrumentationRegistry.getContext().getPackageManager()
+ val packagesHoldingPermission =
+ packageManager
+ .getPackagesHoldingPermissions(
+ arrayOf(android.Manifest.permission.RECORD_SENSITIVE_CONTENT),
+ 0
+ )
+ .map { it.packageName }
+
+ if (packagesHoldingPermission.size > 1) {
+ Assert.fail(
+ "Only one system app on the device is allowed to hold the " +
+ "RECORD_SENSITIVE_CONTENT_DURING_PROJECTION permission, " +
+ "packages holding the permissions are: " +
+ packagesHoldingPermission
+ )
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RemovePermissionTest.java b/tests/cts/permission/src/android/permission/cts/RemovePermissionTest.java
new file mode 100644
index 000000000..915918f71
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RemovePermissionTest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+@AppModeFull(reason = "Instant apps cannot read state of other packages.")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class RemovePermissionTest extends StsExtraBusinessLogicTestCase {
+ private static final String APP_PKG_NAME_BASE =
+ "android.permission.cts.revokepermissionwhenremoved";
+ private static final String ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".AdversarialPermissionDefinerApp";
+ private static final String VICTIM_PERMISSION_DEFINER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".VictimPermissionDefinerApp";
+ private static final String ADVERSARIAL_PERMISSION_USER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".userapp";
+ private static final String RUNTIME_PERMISSION_USER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".runtimepermissionuserapp";
+ private static final String RUNTIME_PERMISSION_DEFINER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".runtimepermissiondefinerapp";
+ private static final String INSTALL_PERMISSION_USER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".installpermissionuserapp";
+ private static final String INSTALL_PERMISSION_DEFINER_PKG_NAME =
+ APP_PKG_NAME_BASE + ".installpermissiondefinerapp";
+ private static final String INSTALL_PERMISSION_ESCALATOR_PKG_NAME =
+ APP_PKG_NAME_BASE + ".installpermissionescalatorapp";
+
+ private static final String TEST_PERMISSION =
+ "android.permission.cts.revokepermissionwhenremoved.TestPermission";
+ private static final String TEST_RUNTIME_PERMISSION =
+ APP_PKG_NAME_BASE + ".TestRuntimePermission";
+ private static final String TEST_INSTALL_PERMISSION =
+ APP_PKG_NAME_BASE + ".TestInstallPermission";
+
+ private static final String ADVERSARIAL_PERMISSION_DEFINER_APK_NAME =
+ "CtsAdversarialPermissionDefinerApp";
+ private static final String ADVERSARIAL_PERMISSION_USER_APK_NAME =
+ "CtsAdversarialPermissionUserApp";
+ private static final String VICTIM_PERMISSION_DEFINER_APK_NAME =
+ "CtsVictimPermissionDefinerApp";
+ private static final String RUNTIME_PERMISSION_DEFINER_APK_NAME =
+ "CtsRuntimePermissionDefinerApp";
+ private static final String RUNTIME_PERMISSION_USER_APK_NAME =
+ "CtsRuntimePermissionUserApp";
+ private static final String INSTALL_PERMISSION_DEFINER_APK_NAME =
+ "CtsInstallPermissionDefinerApp";
+ private static final String INSTALL_PERMISSION_USER_APK_NAME =
+ "CtsInstallPermissionUserApp";
+ private static final String INSTALL_PERMISSION_ESCALATOR_APK_NAME =
+ "CtsInstallPermissionEscalatorApp";
+
+ private Context mContext;
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setContextAndInstrumentation() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ @Before
+ public void wakeUpScreen() {
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+ }
+
+ @After
+ public void cleanUpTestApps() throws Exception {
+ uninstallApp(ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(ADVERSARIAL_PERMISSION_USER_PKG_NAME, true);
+ uninstallApp(VICTIM_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(RUNTIME_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(RUNTIME_PERMISSION_USER_PKG_NAME, true);
+ uninstallApp(INSTALL_PERMISSION_USER_PKG_NAME, true);
+ uninstallApp(INSTALL_PERMISSION_DEFINER_PKG_NAME, true);
+ uninstallApp(INSTALL_PERMISSION_ESCALATOR_PKG_NAME, true);
+ Thread.sleep(5000);
+ }
+
+ private boolean permissionGranted(String pkgName, String permName)
+ throws PackageManager.NameNotFoundException {
+ PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(pkgName,
+ GET_PERMISSIONS);
+
+ for (int i = 0; i < appInfo.requestedPermissions.length; i++) {
+ if (appInfo.requestedPermissions[i].equals(permName)
+ && ((appInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+ != 0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void installApp(String apk) throws InterruptedException {
+ String installResult = SystemUtil.runShellCommandOrThrow(
+ "pm install -r -d /data/local/tmp/cts-permission/" + apk + ".apk");
+ assertEquals("Success", installResult.trim());
+ Thread.sleep(5000);
+ }
+
+ private void uninstallApp(String pkg) throws InterruptedException {
+ uninstallApp(pkg, false);
+ }
+
+ private void uninstallApp(String pkg, boolean cleanUp) throws InterruptedException {
+ String uninstallResult = SystemUtil.runShellCommand("pm uninstall " + pkg);
+ if (!cleanUp) {
+ assertEquals("Success", uninstallResult.trim());
+ Thread.sleep(5000);
+ }
+ }
+
+ private void grantPermission(String pkg, String permission) {
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
+ pkg, permission);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 67319274)
+ public void runtimePermissionShouldBeRevokedIfRemoved() throws Throwable {
+ installApp(ADVERSARIAL_PERMISSION_DEFINER_APK_NAME);
+ installApp(ADVERSARIAL_PERMISSION_USER_APK_NAME);
+
+ grantPermission(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION);
+ assertTrue(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
+
+ // Uninstall app which defines a permission with the same name as in victim app.
+ // Install the victim app.
+ uninstallApp(ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME);
+ installApp(VICTIM_PERMISSION_DEFINER_APK_NAME);
+ assertFalse(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
+ }
+
+ @Test
+ public void runtimePermissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
+ installApp(RUNTIME_PERMISSION_DEFINER_APK_NAME);
+ installApp(RUNTIME_PERMISSION_USER_APK_NAME);
+
+ grantPermission(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION);
+ assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
+
+ // Install app which defines a permission. This is similar to update the app
+ // operation
+ installApp(RUNTIME_PERMISSION_DEFINER_APK_NAME);
+ assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
+ }
+
+ @Test
+ public void runtimePermissionDependencyTest() throws Throwable {
+ installApp(ADVERSARIAL_PERMISSION_USER_APK_NAME);
+ // Should fail to grant permission because its definer is not installed yet
+ try {
+ grantPermission(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION);
+ fail("Should have thrown security exception above");
+ } catch (SecurityException expected) {
+ }
+ assertFalse(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
+ // Now install the permission definer; should be able to grant permission to user package
+ installApp(ADVERSARIAL_PERMISSION_DEFINER_APK_NAME);
+ grantPermission(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION);
+ assertTrue(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
+ // Now uninstall the permission definer; the user packages' permission should be revoked
+ uninstallApp(ADVERSARIAL_PERMISSION_DEFINER_PKG_NAME);
+ assertFalse(permissionGranted(ADVERSARIAL_PERMISSION_USER_PKG_NAME, TEST_PERMISSION));
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 155648771)
+ public void installPermissionShouldBeRevokedIfRemoved() throws Throwable {
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Uninstall the app which defines the install permission, and install another app
+ // redefining it as a runtime permission.
+ uninstallApp(INSTALL_PERMISSION_DEFINER_PKG_NAME);
+ installApp(INSTALL_PERMISSION_ESCALATOR_APK_NAME);
+ assertFalse(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+ }
+
+ @Test
+ public void installPermissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Install the app which defines the install permission again, similar to updating the app.
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+ }
+
+ @Test
+ public void installPermissionDependencyTest() throws Throwable {
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ // Should not have the permission auto-granted
+ assertFalse(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Now install the permission definer; user package should have the permission auto granted
+ installApp(INSTALL_PERMISSION_DEFINER_APK_NAME);
+ installApp(INSTALL_PERMISSION_USER_APK_NAME);
+ assertTrue(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+
+ // Now uninstall the permission definer; the user package's permission should be revoked
+ uninstallApp(INSTALL_PERMISSION_DEFINER_PKG_NAME);
+ assertFalse(permissionGranted(INSTALL_PERMISSION_USER_PKG_NAME, TEST_INSTALL_PERMISSION));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RevokePermissionTest.kt b/tests/cts/permission/src/android/permission/cts/RevokePermissionTest.kt
new file mode 100644
index 000000000..579b03f9c
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RevokePermissionTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts
+
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.READ_CALENDAR
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.os.Process
+import android.platform.test.annotations.AppModeFull
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+class RevokePermissionTest {
+
+ private val APP_PKG_NAME = "android.permission.cts.appthatrequestcustompermission"
+ private val APK =
+ "/data/local/tmp/cts-permission/" +
+ "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk"
+
+ @Before
+ fun installApp() {
+ runShellCommand("pm install -r -g $APK")
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokePermission() {
+ testRevoke(packageName = APP_PKG_NAME, permission = READ_CALENDAR, isGranted = true)
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokeFakePermission() {
+ val fakePermissionName = "FAKE_PERMISSION"
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = fakePermissionName,
+ throwableType = java.lang.IllegalArgumentException::class.java,
+ throwableMessages =
+ listOf(
+ "Unknown permission: $fakePermissionName",
+ "Unknown permission $fakePermissionName"
+ )
+ )
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokeFakePackage() {
+ val fakePackageName = "fake.package.name.which.should.not.exist"
+ assertPackageNotInstalled(fakePackageName)
+ testRevoke(packageName = fakePackageName, permission = READ_CALENDAR)
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokePermissionWithReason() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = READ_CALENDAR,
+ reason = "test reason",
+ isGranted = true
+ )
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokeFakePermissionWithReason() {
+ val fakePermissionName = "FAKE_PERMISSION"
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = fakePermissionName,
+ reason = "test reason",
+ throwableType = java.lang.IllegalArgumentException::class.java,
+ throwableMessages =
+ listOf(
+ "Unknown permission: $fakePermissionName",
+ "Unknown permission $fakePermissionName"
+ )
+ )
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokeFakePackageWithReason() {
+ val fakePackageName = "fake.package.name.which.should.not.exist"
+ assertPackageNotInstalled(fakePackageName)
+ testRevoke(
+ packageName = fakePackageName,
+ permission = READ_CALENDAR,
+ reason = "test reason"
+ )
+ }
+
+ @After
+ fun uninstallApp() {
+ runShellCommand("pm uninstall $APP_PKG_NAME")
+ }
+
+ private fun testRevoke(
+ packageName: String,
+ permission: String,
+ reason: String? = null,
+ isGranted: Boolean = false,
+ throwableType: Class<*>? = null,
+ throwableMessages: List<String> = listOf("")
+ ) {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ val pm = context.packageManager
+
+ if (isGranted) {
+ assertEquals(PERMISSION_GRANTED, pm.checkPermission(READ_CALENDAR, APP_PKG_NAME))
+ }
+
+ runWithShellPermissionIdentity {
+ if (throwableType == null) {
+ if (reason == null) {
+ pm.revokeRuntimePermission(packageName, permission, Process.myUserHandle())
+ } else {
+ pm.revokeRuntimePermission(
+ packageName,
+ permission,
+ Process.myUserHandle(),
+ reason
+ )
+ }
+ } else {
+ try {
+ if (reason == null) {
+ pm.revokeRuntimePermission(packageName, permission, Process.myUserHandle())
+ } else {
+ pm.revokeRuntimePermission(
+ packageName,
+ permission,
+ Process.myUserHandle(),
+ reason
+ )
+ }
+ } catch (t: Throwable) {
+ if (
+ t::class.java.name == throwableType.name &&
+ throwableMessages.any { t.message!!.contains(it) }
+ ) {
+ return@runWithShellPermissionIdentity
+ }
+ throw RuntimeException("Unexpected throwable", t)
+ }
+ throw RuntimeException("revokeRuntimePermission expected to throw.")
+ }
+ }
+ }
+
+ private fun assertPackageNotInstalled(packageName: String) {
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ val pm = context.packageManager
+ try {
+ pm.getPackageInfo(packageName, 0)
+ throw RuntimeException("$packageName exists on this device")
+ } catch (e: PackageManager.NameNotFoundException) {
+ // Expected
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RevokeSawPermissionTest.kt b/tests/cts/permission/src/android/permission/cts/RevokeSawPermissionTest.kt
new file mode 100644
index 000000000..57a2f7fcb
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RevokeSawPermissionTest.kt
@@ -0,0 +1,65 @@
+/*
+ * 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 android.permission.cts
+
+import android.content.pm.PackageManager
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.After
+import org.junit.Assert
+import org.junit.Test
+
+private val APP_PKG_NAME = "android.permission.cts.usesystemalertwindowpermission"
+private val APK_22 = "/data/local/tmp/cts-permission/" + "CtsAppThatRequestsSystemAlertWindow22.apk"
+private val APK_23 = "/data/local/tmp/cts-permission/" + "CtsAppThatRequestsSystemAlertWindow23.apk"
+
+@AppModeFull
+class RevokeSawPermissionTest {
+
+ fun installApp(apk: String) {
+ PermissionUtils.install(apk)
+ }
+
+ @After
+ fun uninstallApp() {
+ PermissionUtils.uninstallApp(APP_PKG_NAME)
+ }
+
+ @AsbSecurityTest(cveBugId = [221040577L])
+ @Test
+ fun testPre23AppsWithSystemAlertWindowGetDeniedOnUpgrade() {
+ installApp(APK_22)
+ assertAppHasPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, true)
+ installApp(APK_23)
+ assertAppHasPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW, false)
+ }
+
+ private fun assertAppHasPermission(permissionName: String, expectPermission: Boolean) {
+ Assert.assertEquals(
+ if (expectPermission) {
+ PackageManager.PERMISSION_GRANTED
+ } else {
+ PackageManager.PERMISSION_DENIED
+ },
+ InstrumentationRegistry.getInstrumentation()
+ .getTargetContext()
+ .packageManager
+ .checkPermission(permissionName, APP_PKG_NAME)
+ )
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RevokeSelfPermissionTest.java b/tests/cts/permission/src/android/permission/cts/RevokeSelfPermissionTest.java
new file mode 100644
index 000000000..674fa2d12
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RevokeSelfPermissionTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.CAMERA;
+import static android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.permission.cts.PermissionUtils.getPermissionFlags;
+import static android.permission.cts.PermissionUtils.grantPermission;
+import static android.permission.cts.PermissionUtils.setPermissionFlags;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.os.Build;
+import android.os.Process;
+import android.platform.test.annotations.AppModeFull;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@AppModeFull(reason = "Null permission info in instant mode")
+public class RevokeSelfPermissionTest {
+ private static final String APP_PKG_NAME =
+ "android.permission.cts.apptotestrevokeselfpermission";
+ private static final String APK =
+ "/data/local/tmp/cts-permission/CtsAppToTestRevokeSelfPermission.apk";
+ private static final long ONE_TIME_TIMEOUT_MILLIS = 500;
+ private static final long ONE_TIME_TIMER_UPPER_GRACE_PERIOD = 5000;
+
+ private final Instrumentation mInstrumentation =
+ InstrumentationRegistry.getInstrumentation();
+ private final Context mContext = mInstrumentation.getTargetContext();
+ private final ActivityManager mActivityManager =
+ mContext.getSystemService(ActivityManager.class);
+ private final UiDevice mUiDevice = UiDevice.getInstance(mInstrumentation);
+ private String mOldOneTimePermissionTimeoutValue;
+ private String mOldScreenOffTimeoutValue;
+ private String mOldSleepTimeoutValue;
+
+ @Before
+ public void wakeUpScreen() {
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+ SystemUtil.runShellCommand("input keyevent 82");
+ mOldScreenOffTimeoutValue = SystemUtil.runShellCommand(
+ "settings get system screen_off_timeout");
+ mOldSleepTimeoutValue = SystemUtil.runShellCommand("settings get secure sleep_timeout");
+ SystemUtil.runShellCommand("settings put system screen_off_timeout -1");
+ SystemUtil.runShellCommand("settings put secure sleep_timeout -1");
+ }
+
+ @Before
+ public void prepareDeviceForOneTime() {
+ runWithShellPermissionIdentity(() -> {
+ mOldOneTimePermissionTimeoutValue = DeviceConfig.getProperty("permissions",
+ "one_time_permissions_timeout_millis");
+ DeviceConfig.setProperty("permissions", "one_time_permissions_timeout_millis",
+ Long.toString(ONE_TIME_TIMEOUT_MILLIS), false);
+ });
+ }
+
+ @After
+ public void uninstallApp() {
+ runShellCommand("pm uninstall " + APP_PKG_NAME);
+ }
+
+ @After
+ public void restoreDeviceForOneTime() {
+ runWithShellPermissionIdentity(() -> {
+ DeviceConfig.setProperty("permissions", "one_time_permissions_timeout_millis",
+ mOldOneTimePermissionTimeoutValue, false);
+ });
+ SystemUtil.runShellCommand("settings put system screen_off_timeout "
+ + mOldScreenOffTimeoutValue);
+ SystemUtil.runShellCommand("settings put secure sleep_timeout " + mOldSleepTimeoutValue);
+ }
+
+ @Test
+ public void testMultiplePermissions() throws Throwable {
+ // Trying to revoke multiple permissions including some from the same permission group
+ // should work.
+ installApp();
+ String[] permissions = new String[] {ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION,
+ CAMERA};
+ for (String permission : permissions) {
+ grantPermission(APP_PKG_NAME, permission);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, permission);
+ }
+ revokePermissions(permissions);
+ placeAppInBackground();
+ for (String permission : permissions) {
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ permission);
+ }
+ uninstallApp();
+ }
+
+ @Test
+ public void testNormalPermission() throws Throwable {
+ // Trying to revoke a normal (non-runtime) permission should not actually revoke it.
+ installApp();
+ revokePermission(HIGH_SAMPLING_RATE_SENSORS);
+ placeAppInBackground();
+ try {
+ waitUntilPermissionRevoked(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ HIGH_SAMPLING_RATE_SENSORS);
+ fail("android.permission.HIGH_SAMPLING_RATE_SENSORS was revoked");
+ } catch (Throwable expected) {
+ assertEquals(HIGH_SAMPLING_RATE_SENSORS + " not revoked",
+ expected.getMessage());
+ }
+ uninstallApp();
+ }
+
+ @Test
+ public void testKillTriggersRevocation() throws Throwable {
+ // Killing the process should start the revocation right away
+ installApp();
+ grantPermission(APP_PKG_NAME, ACCESS_FINE_LOCATION);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_FINE_LOCATION);
+ revokePermission(ACCESS_FINE_LOCATION);
+ killApp();
+ assertDenied(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_FINE_LOCATION);
+ uninstallApp();
+ }
+
+ @Test
+ public void testNoRevocationWhileForeground() throws Throwable {
+ // Even after calling revokeSelfPermissionOnKill, the permission should stay granted while
+ // the package is in the foreground.
+ installApp();
+ grantPermission(APP_PKG_NAME, ACCESS_FINE_LOCATION);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_FINE_LOCATION);
+ revokePermission(ACCESS_FINE_LOCATION);
+ keepAppInForeground(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD);
+ try {
+ waitUntilPermissionRevoked(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_FINE_LOCATION);
+ fail("android.permission.ACCESS_FINE_LOCATION was revoked");
+ } catch (Throwable expected) {
+ assertEquals(ACCESS_FINE_LOCATION + " not revoked",
+ expected.getMessage());
+ }
+ uninstallApp();
+ }
+
+ @Test
+ public void testRevokeLocationPermission() throws Throwable {
+ // Test behavior specific to location group: revoking fine location should not revoke coarse
+ // location, and background location should not be revoked as long as a foreground
+ // permission is still granted
+ installApp();
+ grantPermission(APP_PKG_NAME, ACCESS_COARSE_LOCATION);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_COARSE_LOCATION);
+ grantPermission(APP_PKG_NAME, ACCESS_FINE_LOCATION);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_FINE_LOCATION);
+ grantPermission(APP_PKG_NAME, ACCESS_BACKGROUND_LOCATION);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_BACKGROUND_LOCATION);
+ revokePermission(ACCESS_BACKGROUND_LOCATION);
+ killApp();
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_BACKGROUND_LOCATION);
+ assertGranted(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_COARSE_LOCATION);
+ assertGranted(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_FINE_LOCATION);
+ grantPermission(APP_PKG_NAME, ACCESS_BACKGROUND_LOCATION);
+ setPermissionFlags(APP_PKG_NAME, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_ONE_TIME, 0);
+ assertGranted(ONE_TIME_TIMER_UPPER_GRACE_PERIOD, ACCESS_BACKGROUND_LOCATION);
+ revokePermission(ACCESS_FINE_LOCATION);
+ killApp();
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_FINE_LOCATION);
+ assertGranted(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_COARSE_LOCATION);
+ assertGranted(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_BACKGROUND_LOCATION);
+ revokePermission(ACCESS_COARSE_LOCATION);
+ killApp();
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_COARSE_LOCATION);
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_BACKGROUND_LOCATION);
+ uninstallApp();
+ }
+
+ @Test
+ public void testNoRepromptWhenUserFixed() throws Throwable {
+ // If a permission has been USER_FIXED to not granted, then revoking the permission group
+ // should leave the USER_FIXED flag.
+ installApp();
+ grantPermission(APP_PKG_NAME, ACCESS_FINE_LOCATION);
+ setPermissionFlags(APP_PKG_NAME, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_FIXED,
+ FLAG_PERMISSION_USER_FIXED);
+ revokePermission(ACCESS_FINE_LOCATION);
+ placeAppInBackground();
+ assertDenied(ONE_TIME_TIMEOUT_MILLIS + ONE_TIME_TIMER_UPPER_GRACE_PERIOD,
+ ACCESS_FINE_LOCATION);
+ int flags = getPermissionFlags(APP_PKG_NAME, ACCESS_BACKGROUND_LOCATION);
+ assertEquals(FLAG_PERMISSION_USER_FIXED, flags & FLAG_PERMISSION_USER_FIXED);
+ uninstallApp();
+ }
+
+
+ private void installApp() {
+ runShellCommandOrThrow("pm install -r " + APK);
+ }
+
+ private void keepAppInForeground(long timeoutMillis) {
+ new Thread(() -> {
+ long start = System.currentTimeMillis();
+ while (System.currentTimeMillis() < start + timeoutMillis) {
+ runWithShellPermissionIdentity(() -> {
+ if (mActivityManager.getPackageImportance(APP_PKG_NAME)
+ > IMPORTANCE_FOREGROUND) {
+ runShellCommand("am start-activity -W -n " + APP_PKG_NAME
+ + "/.RevokePermission");
+ }
+ });
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ }
+ }
+ }).start();
+ }
+
+ private void placeAppInBackground() {
+ boolean[] hasExited = {false};
+ try {
+ new Thread(() -> {
+ while (!hasExited[0]) {
+ mUiDevice.pressHome();
+ mUiDevice.pressBack();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }).start();
+ eventually(() -> {
+ runWithShellPermissionIdentity(() -> {
+ if (mActivityManager.getPackageImportance(APP_PKG_NAME)
+ <= IMPORTANCE_FOREGROUND) {
+ throw new AssertionError("Unable to exit application");
+ }
+ });
+ });
+ } finally {
+ hasExited[0] = true;
+ }
+ }
+
+ /**
+ * Start the app. The app will revoke the permission.
+ */
+ private void revokePermission(String permName) {
+ revokePermissions(new String[] { permName });
+ }
+
+ private void revokePermissions(String[] permissions) {
+ runShellCommand("am start-activity -W -n " + APP_PKG_NAME + "/.RevokePermission"
+ + " --esa permissions " + String.join(",", permissions));
+ PackageManager pkgMgr = mContext.getPackageManager();
+ eventually(() -> runWithShellPermissionIdentity(() -> {
+ for (int i = 0; i < permissions.length; i++) {
+ if ((pkgMgr.getPermissionInfo(permissions[i], 0).getProtection()
+ & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+ int permissionFlags = pkgMgr.getPermissionFlags(permissions[i], APP_PKG_NAME,
+ Process.myUserHandle());
+ Assert.assertTrue((permissionFlags & FLAG_PERMISSION_ONE_TIME) != 0);
+ }
+ }
+ }));
+ }
+
+ private void killApp() {
+ runShellCommand("am force-stop " + APP_PKG_NAME);
+ }
+
+ private void assertGrantedState(String s, String permissionName, int permissionGranted,
+ long timeoutMillis) {
+ eventually(() -> Assert.assertEquals(s, permissionGranted,
+ mContext.getPackageManager().checkPermission(permissionName, APP_PKG_NAME)),
+ timeoutMillis);
+ }
+
+ private void assertGranted(long timeoutMillis, String permissionName) {
+ assertGrantedState("Permission was never granted", permissionName,
+ PackageManager.PERMISSION_GRANTED, timeoutMillis);
+ }
+
+ private void assertDenied(long timeoutMillis, String permissionName) {
+ assertGrantedState("Permission was never revoked", permissionName,
+ PackageManager.PERMISSION_DENIED, timeoutMillis);
+ }
+
+ private void waitUntilPermissionRevoked(long timeoutMillis, String permName) throws Throwable {
+ try {
+ eventually(() -> {
+ PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(APP_PKG_NAME,
+ GET_PERMISSIONS);
+
+ for (int i = 0; i < appInfo.requestedPermissions.length; i++) {
+ if (appInfo.requestedPermissions[i].equals(permName)
+ && (
+ (appInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+ == 0)) {
+ return;
+ }
+ }
+
+ fail(permName + " not revoked");
+ }, timeoutMillis);
+ } catch (RuntimeException e) {
+ throw e.getCause();
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/RuntimePermissionPresentationInfoTest.java b/tests/cts/permission/src/android/permission/cts/RuntimePermissionPresentationInfoTest.java
new file mode 100644
index 000000000..9294c0aff
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/RuntimePermissionPresentationInfoTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.permission.RuntimePermissionPresentationInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link RuntimePermissionPresentationInfoTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class RuntimePermissionPresentationInfoTest {
+ @Test
+ public void runtimePermissionLabelSet() {
+ assertThat(new RuntimePermissionPresentationInfo("test", true,
+ true).getLabel()).isEqualTo("test");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void runtimePermissionLabelSetNull() {
+ RuntimePermissionPresentationInfo info = new RuntimePermissionPresentationInfo(null, true,
+ true);
+ }
+
+ @Test
+ public void runtimePermissionGrantedCanBeTrue() {
+ assertThat(new RuntimePermissionPresentationInfo("", true, true).isGranted()).isTrue();
+ }
+
+ @Test
+ public void runtimePermissionGrantedCanBeFalse() {
+ assertThat(new RuntimePermissionPresentationInfo("", false, true).isGranted()).isFalse();
+ }
+
+ @Test
+ public void runtimePermissionStandardCanBeTrue() {
+ assertThat(new RuntimePermissionPresentationInfo("", true, true).isStandard()).isTrue();
+ }
+
+ @Test
+ public void runtimePermissionStandardCanBeFalse() {
+ assertThat(new RuntimePermissionPresentationInfo("", true, false).isStandard()).isFalse();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SafetyCenterUtils.kt b/tests/cts/permission/src/android/permission/cts/SafetyCenterUtils.kt
new file mode 100644
index 000000000..7ca9b138d
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SafetyCenterUtils.kt
@@ -0,0 +1,158 @@
+/*
+ * 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 android.permission.cts
+
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.Context
+import android.content.Intent
+import android.content.res.Resources
+import android.os.Build
+import android.os.UserHandle
+import android.provider.DeviceConfig
+import android.safetycenter.SafetyCenterIssue
+import android.safetycenter.SafetyCenterManager
+import androidx.annotation.RequiresApi
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import com.android.safetycenter.internaldata.SafetyCenterIds
+import com.android.safetycenter.internaldata.SafetyCenterIssueId
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey
+import org.junit.Assert
+
+object SafetyCenterUtils {
+ /** Name of the flag that determines whether SafetyCenter is enabled. */
+ const val PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ /** Returns whether the device supports Safety Center. */
+ @JvmStatic
+ fun deviceSupportsSafetyCenter(context: Context): Boolean {
+ return context.resources.getBoolean(
+ Resources.getSystem().getIdentifier("config_enableSafetyCenter", "bool", "android")
+ )
+ }
+
+ /** Enabled or disable Safety Center */
+ @JvmStatic
+ fun setSafetyCenterEnabled(enabled: Boolean) {
+ setDeviceConfigPrivacyProperty(PROPERTY_SAFETY_CENTER_ENABLED, enabled.toString())
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ @JvmStatic
+ fun startSafetyCenterActivity(context: Context) {
+ context.startActivity(
+ Intent(Intent.ACTION_SAFETY_CENTER)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ }
+
+ @JvmStatic
+ fun assertSafetyCenterStarted() {
+ // CollapsingToolbar title can't be found by text, so using description instead.
+ waitFindObject(By.desc("Security & privacy"))
+ }
+
+ @JvmStatic
+ fun setDeviceConfigPrivacyProperty(
+ propertyName: String,
+ value: String,
+ uiAutomation: UiAutomation = instrumentation.uiAutomation
+ ) {
+ runWithShellPermissionIdentity(uiAutomation) {
+ val valueWasSet =
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ /* name = */ propertyName,
+ /* value = */ value,
+ /* makeDefault = */ false
+ )
+ check(valueWasSet) { "Could not set $propertyName to $value" }
+ }
+ }
+
+ @JvmStatic
+ fun deleteDeviceConfigPrivacyProperty(
+ propertyName: String,
+ uiAutomation: UiAutomation = instrumentation.uiAutomation
+ ) {
+ runWithShellPermissionIdentity(uiAutomation) {
+ DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_PRIVACY, propertyName)
+ }
+ }
+
+ @JvmStatic
+ private fun getSafetyCenterIssues(
+ automation: UiAutomation = instrumentation.uiAutomation
+ ): List<SafetyCenterIssue> {
+ val safetyCenterManager =
+ instrumentation.targetContext.getSystemService(SafetyCenterManager::class.java)
+ val issues = ArrayList<SafetyCenterIssue>()
+ runWithShellPermissionIdentity(automation) {
+ val safetyCenterData = safetyCenterManager!!.safetyCenterData
+ issues.addAll(safetyCenterData.issues)
+ }
+ return issues
+ }
+
+ @JvmStatic
+ fun assertSafetyCenterIssueExist(
+ sourceId: String,
+ issueId: String,
+ issueTypeId: String,
+ automation: UiAutomation = instrumentation.uiAutomation
+ ) {
+ val safetyCenterIssueId = safetyCenterIssueId(sourceId, issueId, issueTypeId)
+ Assert.assertTrue(
+ "Expect issues in safety center",
+ getSafetyCenterIssues(automation).any { safetyCenterIssueId == it.id }
+ )
+ }
+
+ @JvmStatic
+ fun assertSafetyCenterIssueDoesNotExist(
+ sourceId: String,
+ issueId: String,
+ issueTypeId: String,
+ automation: UiAutomation = instrumentation.uiAutomation
+ ) {
+ val safetyCenterIssueId = safetyCenterIssueId(sourceId, issueId, issueTypeId)
+ Assert.assertTrue(
+ "Expect no issue in safety center",
+ getSafetyCenterIssues(automation).none { safetyCenterIssueId == it.id }
+ )
+ }
+
+ private fun safetyCenterIssueId(sourceId: String, sourceIssueId: String, issueTypeId: String) =
+ SafetyCenterIds.encodeToString(
+ SafetyCenterIssueId.newBuilder()
+ .setSafetyCenterIssueKey(
+ SafetyCenterIssueKey.newBuilder()
+ .setSafetySourceId(sourceId)
+ .setSafetySourceIssueId(sourceIssueId)
+ .setUserId(UserHandle.myUserId())
+ .build()
+ )
+ .setIssueTypeId(issueTypeId)
+ .build()
+ )
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SdkSandboxPermissionTest.java b/tests/cts/permission/src/android/permission/cts/SdkSandboxPermissionTest.java
new file mode 100644
index 000000000..88fcaec45
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SdkSandboxPermissionTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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 android.permission.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Process;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for permission handling for sdk sandbox uid range.
+ */
+@AppModeFull(reason = "Instant apps can't access PermissionManager")
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class SdkSandboxPermissionTest {
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ public void testSdkSandboxHasInternetPermission() throws Exception {
+ final Context ctx = getInstrumentation().getContext();
+ int ret = ctx.checkPermission(
+ Manifest.permission.INTERNET,
+ /* pid= */ -1 /* invalid pid */,
+ Process.toSdkSandboxUid(19999));
+ assertThat(ret).isEqualTo(PackageManager.PERMISSION_GRANTED);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ public void testSdkSandboxDoesNotHaveFineLocationPermission() throws Exception {
+ final Context ctx = getInstrumentation().getContext();
+ int ret = ctx.checkPermission(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ /* pid= */ -1 /* invalid pid */,
+ Process.toSdkSandboxUid(19999));
+ assertThat(ret).isEqualTo(PackageManager.PERMISSION_DENIED);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SecureElementPermissionTest.java b/tests/cts/permission/src/android/permission/cts/SecureElementPermissionTest.java
new file mode 100644
index 000000000..1f04b1ccf
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SecureElementPermissionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static org.junit.Assert.fail;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+@RunWith(JUnit4.class)
+public final class SecureElementPermissionTest {
+ // Needed because SECURE_ELEMENT_PRIVILEGED_PERMISSION is a systemapi
+ public static final String SECURE_ELEMENT_PRIVILEGED_PERMISSION =
+ "android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION";
+
+ @Test
+ public void testSecureElementPrivilegedPermission() {
+ PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+
+ List<Integer> specialUids = Arrays.asList(Process.SYSTEM_UID, Process.PHONE_UID);
+
+ List<PackageInfo> holding = pm.getPackagesHoldingPermissions(
+ new String[] { SECURE_ELEMENT_PRIVILEGED_PERMISSION },
+ PackageManager.MATCH_DISABLED_COMPONENTS);
+
+ List<Integer> nonSpecialPackages = holding.stream()
+ .map(pi -> {
+ try {
+ return pm.getPackageUid(pi.packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return Process.INVALID_UID;
+ }
+ })
+ .filter(uid -> !specialUids.contains(uid))
+ .collect(Collectors.toList());
+
+ if (nonSpecialPackages.size() > 1) {
+ fail("Only one app on the device is allowed to hold the " +
+ "SECURE_ELEMENT_PRIVILEGED_OPERATION permission.");
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/ServicePermissionTest.java b/tests/cts/permission/src/android/permission/cts/ServicePermissionTest.java
new file mode 100644
index 000000000..6c10f1d31
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ServicePermissionTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The security designs of many system features require that a special
+ * permission is only ever granted to the core system (typically
+ * {@code system_server}), since it's the only process that should be binding
+ * into sensitive app code.
+ * <p>
+ * No apps outside the {@code system_server} should <em>ever</em> attempt to
+ * acquire these permissions.
+ */
+public class ServicePermissionTest extends AndroidTestCase {
+ public static String[] sServicePermissions = {
+ android.Manifest.permission.ACCOUNT_MANAGER,
+ android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
+ android.Manifest.permission.BIND_AUTOFILL_SERVICE,
+ android.Manifest.permission.BIND_CHOOSER_TARGET_SERVICE,
+ android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE,
+ // android.Manifest.permission.BIND_DEVICE_ADMIN,
+ android.Manifest.permission.BIND_DREAM_SERVICE,
+ android.Manifest.permission.BIND_INPUT_METHOD,
+ android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE,
+ // android.Manifest.permission.BIND_NFC_SERVICE,
+ android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE,
+ android.Manifest.permission.BIND_PRINT_SERVICE,
+ // android.Manifest.permission.BIND_QUICK_SETTINGS_TILE,
+ android.Manifest.permission.BIND_TEXT_SERVICE,
+ android.Manifest.permission.BIND_VOICE_INTERACTION,
+ android.Manifest.permission.BIND_VPN_SERVICE,
+ android.Manifest.permission.BIND_VR_LISTENER_SERVICE,
+ };
+
+ public void testServicePermissions() {
+ final PackageManager pm = getContext().getPackageManager();
+
+ final List<String> failures = new ArrayList<>();
+ for (String perm : sServicePermissions) {
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(
+ new String[] { perm }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ for (PackageInfo pi : holding) {
+ if (!Objects.equals("android", pi.packageName)) {
+ failures.add(perm + " held by " + pi.packageName);
+ }
+ }
+ }
+ if (!failures.isEmpty()) {
+ fail("Found permissions granted to packages outside of the core system: "
+ + failures.toString());
+ }
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java b/tests/cts/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
new file mode 100644
index 000000000..0c1c885be
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.content.Context.DEVICE_POLICY_SERVICE;
+import static android.content.Context.SHORTCUT_SERVICE;
+import static android.content.Context.USB_SERVICE;
+import static android.content.Context.WALLPAPER_SERVICE;
+import static android.content.Context.WIFI_AWARE_SERVICE;
+import static android.content.Context.WIFI_P2P_SERVICE;
+import static android.content.Context.WIFI_SERVICE;
+
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.platform.test.annotations.AppModeInstant;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Some services are not available to instant apps, see {@link Context#getSystemService}.
+ */
+@AppModeInstant
+@RunWith(AndroidJUnit4.class)
+public class ServicesInstantAppsCannotAccessTests {
+ @Test
+ public void cannotGetDevicePolicyManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ DEVICE_POLICY_SERVICE));
+ }
+
+ @Test
+ public void cannotGetShortcutManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ SHORTCUT_SERVICE));
+ }
+
+ @Test
+ public void cannotGetUsbManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ USB_SERVICE));
+ }
+
+ @Test
+ public void cannotGetWallpaperManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ WALLPAPER_SERVICE));
+ }
+
+ @Test
+ public void cannotGetWifiP2pManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ WIFI_P2P_SERVICE));
+ }
+
+ @Test
+ public void cannotGetWifiManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ WIFI_SERVICE));
+ }
+
+ @Test
+ public void cannotGetWifiAwareManager() {
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ WIFI_AWARE_SERVICE));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SharedUidPermissionsTest.java b/tests/cts/permission/src/android/permission/cts/SharedUidPermissionsTest.java
new file mode 100644
index 000000000..4966a870b
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SharedUidPermissionsTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.INTERNET;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.permission.cts.PermissionUtils.grantPermission;
+import static android.permission.cts.PermissionUtils.install;
+import static android.permission.cts.PermissionUtils.isPermissionGranted;
+import static android.permission.cts.PermissionUtils.revokePermission;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot read properties of other packages which is needed "
+ + "to grant permissions to them.")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class SharedUidPermissionsTest {
+ /** The package name of all apps used in the test */
+ private static final String PKG_THAT_REQUESTS_PERMISSIONS =
+ "android.permission.cts.appthatrequestpermission";
+ private static final String PKG_THAT_REQUESTS_NO_PERMISSIONS =
+ "android.permission.cts.appthatrequestnopermission";
+
+ private static final String TMP_DIR = "/data/local/tmp/cts-permission/";
+ private static final String APK_THAT_REQUESTS_PERMISSIONS =
+ TMP_DIR + "CtsAppWithSharedUidThatRequestsPermissions.apk";
+ private static final String APK_THAT_REQUESTS_NO_PERMISSIONS =
+ TMP_DIR + "CtsAppWithSharedUidThatRequestsNoPermissions.apk";
+
+ @Before
+ @After
+ public void uninstallTestApps() {
+ uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+ uninstallApp(PKG_THAT_REQUESTS_NO_PERMISSIONS);
+ }
+
+ @Test
+ public void packageGainsRuntimePermissionsWhenJoiningSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+ }
+
+ @Test
+ public void packageGainsNormalPermissionsWhenJoiningSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, INTERNET)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, INTERNET)).isTrue();
+ }
+
+ @Test
+ public void grantingRuntimePermissionAffectsAllPackageInSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+ }
+
+ @Test
+ public void revokingRuntimePermissionAffectsAllPackageInSharedUid() throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ revokePermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isFalse();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+ }
+
+ @Test
+ public void runtimePermissionsCanBeRevokedOnPackageThatDoesNotDeclarePermission()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ revokePermission(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isFalse();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+ }
+
+ @Test
+ @FlakyTest
+ public void runtimePermissionsCanBeGrantedOnPackageThatDoesNotDeclarePermission()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS)).isTrue();
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isTrue();
+ }
+
+ @Test
+ public void sharedUidLoosesRuntimePermissionWhenLastAppDeclaringItGetsUninstalled()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ grantPermission(PKG_THAT_REQUESTS_PERMISSIONS, READ_CONTACTS);
+ uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, READ_CONTACTS)).isFalse();
+ }
+
+ @Test
+ @FlakyTest
+ public void sharedUidLoosesNormalPermissionWhenLastAppDeclaringItGetsUninstalled()
+ throws Exception {
+ install(APK_THAT_REQUESTS_PERMISSIONS);
+ install(APK_THAT_REQUESTS_NO_PERMISSIONS);
+ uninstallApp(PKG_THAT_REQUESTS_PERMISSIONS);
+
+ assertThat(isPermissionGranted(PKG_THAT_REQUESTS_NO_PERMISSIONS, INTERNET)).isFalse();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/ShellCommandPermissionTest.java b/tests/cts/permission/src/android/permission/cts/ShellCommandPermissionTest.java
new file mode 100644
index 000000000..54562d5ee
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ShellCommandPermissionTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test shell command capability enforcement
+ */
+@RunWith(AndroidJUnit4.class)
+public class ShellCommandPermissionTest {
+
+ static final int EXPECTED_ERROR_CODE = 255;
+
+ /**
+ * Runs the given command, waits for it to exit, and verifies the return
+ * code indicates failure.
+ */
+ private void executeShellCommandAndWaitForError(String command)
+ throws Exception {
+ try {
+ java.lang.Process proc = Runtime.getRuntime().exec(command);
+ assertThat(proc.waitFor()).isEqualTo(EXPECTED_ERROR_CODE);
+ } catch (InterruptedException e) {
+ fail("Unsuccessful shell command");
+ }
+ }
+
+ @Test
+ public void testTraceIpc() throws Exception {
+ executeShellCommandAndWaitForError(
+ "cmd activity trace-ipc stop --dump-file /data/system/last-fstrim");
+ }
+
+ @Test
+ public void testDumpheap() throws Exception {
+ executeShellCommandAndWaitForError(
+ "cmd activity dumpheap system_server /data/system/last-fstrim");
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/ShellPermissionTest.java b/tests/cts/permission/src/android/permission/cts/ShellPermissionTest.java
new file mode 100644
index 000000000..c4c66564c
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/ShellPermissionTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SystemUserOnly;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests that shell has acceptable permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ShellPermissionTest {
+ private static final String LOG_TAG = ShellPermissionTest.class.getSimpleName();
+
+ /** Permissions that shell is NOT permitted to have. */
+ private static final String[] BLACKLISTED_PERMISSIONS = {
+ "android.permission.MANAGE_USERS",
+ "android.permission.NETWORK_STACK",
+ "android.permission.MANAGE_WIFI_COUNTRY_CODE",
+ };
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+ /**
+ * Verify that the shell uid does not have any of the permissions listed in
+ * {@link #BLACKLISTED_PERMISSIONS}.
+ */
+ @Test
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages. Also the shell "
+ + "is never an instant app, hence this test does not matter for instant apps.")
+ public void testBlacklistedPermissions() throws Exception {
+ final Set<String> blacklist = new HashSet<>(Arrays.asList(BLACKLISTED_PERMISSIONS));
+
+ final PackageManager pm = sContext.getPackageManager();
+ int uid = UserHandle.getUid(UserHandle.myUserId(), UserHandle.getAppId(Process.SHELL_UID));
+ final String[] pkgs = pm.getPackagesForUid(uid);
+ Log.d(LOG_TAG, "SHELL_UID: " + Process.SHELL_UID + " myUserId: "
+ + UserHandle.myUserId() + " uid: " + uid + " pkgs.length: " + pkgs.length);
+ assertNotNull("No SHELL packages were found", pkgs);
+ assertNotEquals("SHELL package list had 0 size", 0, pkgs.length);
+ String pkg = pkgs[0];
+
+ final PackageInfo packageInfo = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
+ assertNotNull("No permissions found for " + pkg, packageInfo.requestedPermissions);
+
+ for (String permission : packageInfo.requestedPermissions) {
+ Log.d(LOG_TAG, "SHELL as " + pkg + " uses permission " + permission + " uid: "
+ + uid);
+ assertFalse("SHELL as " + pkg + " contains the illegal permission " + permission,
+ blacklist.contains(permission));
+ }
+ }
+
+ @Test
+ @SystemUserOnly
+ @AppModeFull(reason = "Instant apps cannot read properties of other packages. Also the shell "
+ + "is never an instant app, hence this test does not matter for instant apps.")
+ public void testBlacklistedPermissionsForSystemUser() throws Exception {
+ testBlacklistedPermissions();
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SmsManagerPermissionTest.java b/tests/cts/permission/src/android/permission/cts/SmsManagerPermissionTest.java
new file mode 100644
index 000000000..dd0d9f234
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SmsManagerPermissionTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Test that sending SMS and MMS messages requires permissions.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsManagerPermissionTest {
+
+ private static final String SOURCE_ADDRESS = "+15550000000";
+ private static final String DESTINATION_ADDRESS = "+15550000001";
+
+ private boolean mHasTelephony;
+ private SmsManager mSmsManager;
+ private Context mContext;
+ private TelephonyManager mTelephonyManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ mHasTelephony = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY);
+ assumeTrue(mHasTelephony); // Don't run these tests if FEATURE_TELEPHONY is not available.
+
+ Log.d("SmsManagerPermissionTest", "mSubId=" + subId);
+
+ mSmsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
+ assertNotNull(mSmsManager);
+ }
+
+ @Test
+ public void testSendTextMessage() {
+ assertFalse("[RERUN] Device does not have SIM card. Use a suitable SIM Card.",
+ isSimCardAbsent());
+
+ assertThrows(SecurityException.class, () -> mSmsManager.sendTextMessage(
+ DESTINATION_ADDRESS, SOURCE_ADDRESS, "Message text", null, null));
+ }
+
+ @Test
+ public void testSendTextMessageWithoutPersisting() {
+ assertFalse("[RERUN] Device does not have SIM card. Use a suitable SIM Card.",
+ isSimCardAbsent());
+
+ assertThrows(SecurityException.class, () -> mSmsManager.sendTextMessageWithoutPersisting(
+ DESTINATION_ADDRESS, SOURCE_ADDRESS, "Message text", null, null));
+ }
+
+ @Test
+ public void testSendMultipartTextMessage() {
+ assertFalse("[RERUN] Device does not have SIM card. Use a suitable SIM Card.",
+ isSimCardAbsent());
+
+ ArrayList<String> messageParts = new ArrayList<>();
+ messageParts.add("Message text");
+ assertThrows(SecurityException.class, () -> mSmsManager.sendMultipartTextMessage(
+ DESTINATION_ADDRESS, SOURCE_ADDRESS, messageParts, null, null));
+ }
+
+ @Test
+ public void testSendDataMessage() {
+ assertFalse("[RERUN] Device does not have SIM card. Use a suitable SIM Card.",
+ isSimCardAbsent());
+
+ assertThrows(SecurityException.class, () -> mSmsManager.sendDataMessage(
+ DESTINATION_ADDRESS, SOURCE_ADDRESS, (short) 1, new byte[]{0, 0, 0}, null, null));
+ }
+
+ @Test
+ public void testSendMultimediaMessage() {
+ assertFalse("[RERUN] Device does not have SIM card. Use a suitable SIM Card.",
+ isSimCardAbsent());
+
+ // Ideally we would provide an Uri to an existing resource, to make sure the
+ // SecurityException is not due to the invalid Uri.
+ Uri uri = Uri.parse("android.resource://android.permission.cts/some-image.png");
+ assertThrows(SecurityException.class,
+ () -> mSmsManager.sendMultimediaMessage(mContext, uri, "", null, null));
+ }
+
+ private boolean isSimCardAbsent() {
+ return ((mTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_ABSENT)
+ || (SubscriptionManager.getDefaultSmsSubscriptionId()
+ == SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SplitPermissionTest.java b/tests/cts/permission/src/android/permission/cts/SplitPermissionTest.java
new file mode 100644
index 000000000..a509b3bfe
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SplitPermissionTest.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.READ_CALL_LOG;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.permission.cts.PermissionUtils.getAppOp;
+import static android.permission.cts.PermissionUtils.getPermissionFlags;
+import static android.permission.cts.PermissionUtils.getPermissions;
+import static android.permission.cts.PermissionUtils.grantPermission;
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.revokePermission;
+import static android.permission.cts.PermissionUtils.setPermissionFlags;
+import static android.permission.cts.PermissionUtils.uninstallApp;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.UiAutomation;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SystemUserOnly;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests how split permissions behave.
+ *
+ * <ul>
+ * <li>Default permission grant behavior</li>
+ * <li>Changes to the grant state during upgrade of apps with split permissions</li>
+ * <li>Special behavior of background location</li>
+ * </ul>
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot read state of other packages.")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class SplitPermissionTest {
+ /** The package name of all apps used in the test */
+ private static final String APP_PKG = "android.permission.cts.appthatrequestpermission";
+
+ private static final String TMP_DIR = "/data/local/tmp/cts-permission/";
+ private static final String APK_CONTACTS_16 =
+ TMP_DIR + "CtsAppThatRequestsContactsPermission16.apk";
+ private static final String APK_CONTACTS_15 =
+ TMP_DIR + "CtsAppThatRequestsContactsPermission15.apk";
+ private static final String APK_CONTACTS_CALLLOG_16 =
+ TMP_DIR + "CtsAppThatRequestsContactsAndCallLogPermission16.apk";
+ private static final String APK_STORAGE_29 =
+ TMP_DIR + "CtsAppThatRequestsStoragePermission29.apk";
+ private static final String APK_STORAGE_28 =
+ TMP_DIR + "CtsAppThatRequestsStoragePermission28.apk";
+ private static final String APK_LOCATION_29 =
+ TMP_DIR + "CtsAppThatRequestsLocationPermission29.apk";
+ private static final String APK_LOCATION_28 =
+ TMP_DIR + "CtsAppThatRequestsLocationPermission28.apk";
+ private static final String APK_LOCATION_22 =
+ TMP_DIR + "CtsAppThatRequestsLocationPermission22.apk";
+ private static final String APK_LOCATION_BACKGROUND_28 =
+ TMP_DIR + "CtsAppThatRequestsLocationAndBackgroundPermission28.apk";
+ private static final String APK_LOCATION_BACKGROUND_29 =
+ TMP_DIR + "CtsAppThatRequestsLocationAndBackgroundPermission29.apk";
+ private static final String APK_SHARED_UID_LOCATION_29 =
+ TMP_DIR + "CtsAppWithSharedUidThatRequestsLocationPermission29.apk";
+ private static final String APK_SHARED_UID_LOCATION_28 =
+ TMP_DIR + "CtsAppWithSharedUidThatRequestsLocationPermission28.apk";
+
+ private static final UiAutomation sUiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ /**
+ * Assert that {@link #APP_PKG} requests a certain permission.
+ *
+ * @param permName The permission that needs to be requested
+ */
+ private void assertRequestsPermission(@NonNull String permName) throws Exception {
+ assertThat(getPermissions(APP_PKG)).contains(permName);
+ }
+
+ /**
+ * Assert that {@link #APP_PKG} <u>does not</u> request a certain permission.
+ *
+ * @param permName The permission that needs to be not requested
+ */
+ private void assertNotRequestsPermission(@NonNull String permName) throws Exception {
+ assertThat(getPermissions(APP_PKG)).doesNotContain(permName);
+ }
+
+ /**
+ * Assert that a permission is granted to {@link #APP_PKG}.
+ *
+ * @param permName The permission that needs to be granted
+ */
+ private void assertPermissionGranted(@NonNull String permName) throws Exception {
+ eventually(() -> assertWithMessage(permName + " is granted").that(
+ isGranted(APP_PKG, permName)).isTrue());
+ }
+
+ /**
+ * Assert that a permission is <u>not </u> granted to {@link #APP_PKG}.
+ *
+ * @param permName The permission that should not be granted
+ */
+ private void assertPermissionRevoked(@NonNull String permName) throws Exception {
+ assertWithMessage(permName + " is granted").that(isGranted(APP_PKG, permName)).isFalse();
+ }
+
+ /**
+ * Install an APK.
+ *
+ * @param apkFile The apk to install
+ */
+ public void install(@NonNull String apkFile) {
+ PermissionUtils.install(apkFile);
+ }
+
+ @After
+ public void uninstallTestApp() {
+ uninstallApp(APP_PKG);
+ }
+
+ /**
+ * Apps with a targetSDK after the split should <u>not</u> have been added implicitly the new
+ * permission.
+ */
+ @Test
+ public void permissionsDoNotSplitWithHighTargetSDK() throws Exception {
+ install(APK_LOCATION_29);
+
+ assertRequestsPermission(ACCESS_COARSE_LOCATION);
+ assertNotRequestsPermission(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * Apps with a targetSDK after the split should <u>not</u> have been added implicitly the new
+ * permission.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void permissionsDoNotSplitWithHighTargetSDKPreM() throws Exception {
+ install(APK_CONTACTS_16);
+
+ assertRequestsPermission(READ_CONTACTS);
+ assertNotRequestsPermission(READ_CALL_LOG);
+ }
+
+ /**
+ * Apps with a targetSDK before the split should have been added implicitly the new permission.
+ */
+ @Test
+ public void permissionsSplitWithLowTargetSDK() throws Exception {
+ install(APK_LOCATION_28);
+
+ assertRequestsPermission(ACCESS_COARSE_LOCATION);
+ assertRequestsPermission(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * Apps with a targetSDK before the split should have been added implicitly the new permission.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void permissionsSplitWithLowTargetSDKPreM() throws Exception {
+ install(APK_CONTACTS_15);
+
+ assertRequestsPermission(READ_CONTACTS);
+ assertRequestsPermission(READ_CALL_LOG);
+ }
+
+ /**
+ * Permissions are revoked by default for post-M apps
+ */
+ @Test
+ public void nonInheritedStateHighTargetSDK() throws Exception {
+ install(APK_LOCATION_29);
+
+ assertPermissionRevoked(ACCESS_COARSE_LOCATION);
+ }
+
+ /**
+ * Permissions are granted by default for pre-M apps
+ */
+ @Test
+ public void nonInheritedStateHighLowTargetSDKPreM() throws Exception {
+ install(APK_CONTACTS_15);
+
+ assertPermissionGranted(READ_CONTACTS);
+ }
+
+ /**
+ * Permissions are revoked by default for post-M apps. This also applies to permissions added
+ * implicitly due to splits.
+ */
+ @Test
+ public void nonInheritedStateLowTargetSDK() throws Exception {
+ install(APK_LOCATION_28);
+
+ assertPermissionRevoked(ACCESS_COARSE_LOCATION);
+ assertPermissionRevoked(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * Permissions are granted by default for pre-M apps. This also applies to permissions added
+ * implicitly due to splits.
+ */
+ @Test
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_OUTGOING_CALLS user restriction")
+ public void nonInheritedStateLowTargetSDKPreM() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_OUTGOING_CALLS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ install(APK_CONTACTS_15);
+
+ assertPermissionGranted(READ_CONTACTS);
+ assertPermissionGranted(READ_CALL_LOG);
+ }
+
+ /**
+ * The background location permission granted by default for pre-M apps.
+ */
+ @Test
+ public void backgroundLocationPermissionDefaultGrantPreM() throws Exception {
+ install(APK_LOCATION_22);
+
+ assertPermissionGranted(ACCESS_COARSE_LOCATION);
+ assertPermissionGranted(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * If a permission was granted before the split happens, the new permission should inherit the
+ * granted state.
+ */
+ @FlakyTest(bugId = 152580253)
+ @MtsIgnore(bugId = 152580253)
+ @Test
+ public void inheritGrantedPermissionState() throws Exception {
+ install(APK_LOCATION_29);
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ install(APK_LOCATION_28);
+
+ assertPermissionGranted(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * If a permission was granted before the split happens, the new permission should inherit the
+ * granted state.
+ *
+ * <p>App using a shared uid
+ */
+ @Test
+ public void inheritGrantedPermissionStateSharedUidApp() throws Exception {
+ install(APK_SHARED_UID_LOCATION_29);
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ install(APK_SHARED_UID_LOCATION_28);
+
+ assertPermissionGranted(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * If a permission has flags before the split happens, the new permission should inherit the
+ * flags.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @FlakyTest(bugId = 152580253)
+ @MtsIgnore(bugId = 152580253)
+ @Test
+ public void inheritFlagsPreM() {
+ install(APK_CONTACTS_16);
+ setPermissionFlags(APP_PKG, READ_CONTACTS, FLAG_PERMISSION_USER_SET,
+ FLAG_PERMISSION_USER_SET);
+
+ install(APK_CONTACTS_15);
+
+ assertEquals(FLAG_PERMISSION_USER_SET,
+ getPermissionFlags(APP_PKG, READ_CALL_LOG) & FLAG_PERMISSION_USER_SET);
+ }
+
+ /**
+ * If a permission has flags before the split happens, the new permission should inherit the
+ * flags.
+ */
+ @FlakyTest(bugId = 152580253)
+ @MtsIgnore(bugId = 152580253)
+ @Test
+ public void inheritFlags() {
+ install(APK_LOCATION_29);
+ setPermissionFlags(APP_PKG, ACCESS_COARSE_LOCATION, FLAG_PERMISSION_USER_SET,
+ FLAG_PERMISSION_USER_SET);
+
+ install(APK_LOCATION_28);
+
+ assertEquals(FLAG_PERMISSION_USER_SET,
+ getPermissionFlags(APP_PKG, ACCESS_BACKGROUND_LOCATION) & FLAG_PERMISSION_USER_SET);
+ }
+
+ /**
+ * If a permission was granted before the split happens, the new permission should inherit the
+ * granted state.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_OUTGOING_CALLS user restriction")
+ public void inheritGrantedPermissionStatePreM() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_OUTGOING_CALLS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ install(APK_CONTACTS_16);
+
+ install(APK_CONTACTS_15);
+
+ assertPermissionGranted(READ_CALL_LOG);
+ }
+
+ /**
+ * If a permission was revoked before the split happens, the new permission should inherit the
+ * revoked state.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void inheritRevokedPermissionState() throws Exception {
+ install(APK_LOCATION_29);
+
+ install(APK_LOCATION_28);
+
+ assertPermissionRevoked(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * If a permission was revoked before the split happens, the new permission should inherit the
+ * revoked state.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void inheritRevokedPermissionStatePreM() throws Exception {
+ install(APK_CONTACTS_16);
+ revokePermission(APP_PKG, READ_CONTACTS);
+
+ install(APK_CONTACTS_15);
+
+ /*
+ * Ideally the new permission should inherit from it's base permission, but this is tricky
+ * to implement.
+ * The new permissions need to be reviewed, hence the pre-review state really does not
+ * matter anyway.
+ */
+ // assertPermissionRevoked(READ_CALL_LOG);
+ assertThat(getPermissionFlags(APP_PKG, READ_CALL_LOG)
+ & FLAG_PERMISSION_REVIEW_REQUIRED).isEqualTo(FLAG_PERMISSION_REVIEW_REQUIRED);
+ }
+
+ /**
+ * It should be possible to grant a permission implicitly added due to a split.
+ */
+ @Test
+ public void grantNewSplitPermissionState() throws Exception {
+ install(APK_LOCATION_28);
+
+ // Background permission can only be granted together with foreground permission
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ grantPermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ assertPermissionGranted(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * It should be possible to grant a permission implicitly added due to a split.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_OUTGOING_CALLS user restriction")
+ public void grantNewSplitPermissionStatePreM() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_OUTGOING_CALLS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ install(APK_CONTACTS_15);
+ revokePermission(APP_PKG, READ_CONTACTS);
+
+ grantPermission(APP_PKG, READ_CALL_LOG);
+
+ assertPermissionGranted(READ_CALL_LOG);
+ }
+
+ /**
+ * It should be possible to revoke a permission implicitly added due to a split.
+ */
+ @Test
+ public void revokeNewSplitPermissionState() throws Exception {
+ install(APK_LOCATION_28);
+
+ // Background permission can only be granted together with foreground permission
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ grantPermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ revokePermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ assertPermissionRevoked(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * It should be possible to revoke a permission implicitly added due to a split.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void revokeNewSplitPermissionStatePreM() throws Exception {
+ install(APK_CONTACTS_15);
+
+ revokePermission(APP_PKG, READ_CALL_LOG);
+
+ assertPermissionRevoked(READ_CALL_LOG);
+ }
+
+ /**
+ * An implicit permission should get revoked when the app gets updated and now requests the
+ * permission.
+ */
+ @Test
+ public void newPermissionGetRevokedOnUpgrade() throws Exception {
+ install(APK_LOCATION_28);
+
+ // Background permission can only be granted together with foreground permission
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ grantPermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ install(APK_LOCATION_BACKGROUND_29);
+
+ assertPermissionRevoked(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * An implicit background permission should get revoked when the app gets updated and now
+ * requests the permission. Revoking a background permission should have changed the app-op of
+ * the foreground permission.
+ */
+ @Test
+ public void newBackgroundPermissionGetRevokedOnUpgrade() throws Exception {
+ install(APK_LOCATION_28);
+
+ // Background permission can only be granted together with foreground permission
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ grantPermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ install(APK_LOCATION_BACKGROUND_29);
+
+ eventually(() -> assertWithMessage("foreground app-op").that(
+ getAppOp(APP_PKG, ACCESS_COARSE_LOCATION)).isEqualTo(MODE_FOREGROUND));
+ }
+
+ /**
+ * An implicit permission should get revoked when the app gets updated and now requests the
+ * permission. This even happens if the app is not targeting the SDK the permission was split
+ * in.
+ */
+ @Test
+ public void newPermissionGetRevokedOnUpgradeBeforeSplitSDK() throws Exception {
+ install(APK_LOCATION_28);
+
+ // Background permission can only be granted together with foreground permission
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+ grantPermission(APP_PKG, ACCESS_BACKGROUND_LOCATION);
+
+ // Background location was introduced in SDK 29. Hence an app targeting 28 is usually
+ // unaware of this permission. If the app declares that it is aware by adding the permission
+ // in the manifest the permission will get revoked. This allows the app to request the
+ // permission from the user.
+ install(APK_LOCATION_BACKGROUND_28);
+
+ assertPermissionRevoked(ACCESS_BACKGROUND_LOCATION);
+ }
+
+ /**
+ * An implicit permission should <u>not</u> get revoked when the app gets updated as pre-M apps
+ * cannot deal with revoked permissions. Hence only the user should ever explicitly do that.
+ */
+ @Test
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_OUTGOING_CALLS user restriction")
+ public void newPermissionGetRevokedOnUpgradePreM() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_OUTGOING_CALLS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ install(APK_CONTACTS_15);
+
+ install(APK_CONTACTS_CALLLOG_16);
+
+ assertPermissionGranted(READ_CALL_LOG);
+ }
+
+ /**
+ * When a requested permission was granted before upgrade it should still be granted.
+ */
+ @Test
+ public void oldPermissionStaysGrantedOnUpgrade() throws Exception {
+ install(APK_LOCATION_28);
+ grantPermission(APP_PKG, ACCESS_COARSE_LOCATION);
+
+ install(APK_LOCATION_BACKGROUND_29);
+
+ assertPermissionGranted(ACCESS_COARSE_LOCATION);
+ }
+
+ /**
+ * When a requested permission was granted before upgrade it should still be granted.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void oldPermissionStaysGrantedOnUpgradePreM() throws Exception {
+ install(APK_CONTACTS_15);
+
+ install(APK_CONTACTS_CALLLOG_16);
+
+ assertPermissionGranted(READ_CONTACTS);
+ }
+
+ /**
+ * When a requested permission was revoked before upgrade it should still be revoked.
+ */
+ @Test
+ public void oldPermissionStaysRevokedOnUpgrade() throws Exception {
+ install(APK_LOCATION_28);
+
+ install(APK_LOCATION_BACKGROUND_29);
+
+ assertPermissionRevoked(ACCESS_COARSE_LOCATION);
+ }
+
+ /**
+ * When a requested permission was revoked before upgrade it should still be revoked.
+ *
+ * <p>(Pre-M version of test)
+ */
+ @Test
+ public void oldPermissionStaysRevokedOnUpgradePreM() throws Exception {
+ install(APK_CONTACTS_15);
+ revokePermission(APP_PKG, READ_CONTACTS);
+
+ install(APK_CONTACTS_CALLLOG_16);
+
+ assertPermissionRevoked(READ_CONTACTS);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
new file mode 100755
index 000000000..776a1065e
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
+import static android.Manifest.permission.BLUETOOTH;
+import static android.Manifest.permission.BLUETOOTH_ADMIN;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_SCAN;
+import static android.Manifest.permission.BODY_SENSORS;
+import static android.Manifest.permission.BODY_SENSORS_BACKGROUND;
+import static android.Manifest.permission.READ_CALL_LOG;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_MEDIA_AUDIO;
+import static android.Manifest.permission.READ_MEDIA_IMAGES;
+import static android.Manifest.permission.READ_MEDIA_VIDEO;
+import static android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.Manifest.permission.WRITE_CALL_LOG;
+import static android.Manifest.permission.WRITE_CONTACTS;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.os.Build;
+import android.permission.PermissionManager;
+import android.permission.PermissionManager.SplitPermissionInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+public class SplitPermissionsSystemTest {
+
+ private static final int NO_TARGET = Build.VERSION_CODES.CUR_DEVELOPMENT + 1;
+
+ private List<SplitPermissionInfo> mSplitPermissions;
+
+ @Before
+ public void before() {
+ Context context = InstrumentationRegistry.getContext();
+ PermissionManager permissionManager = (PermissionManager) context.getSystemService(
+ Context.PERMISSION_SERVICE);
+ mSplitPermissions = permissionManager.getSplitPermissions();
+ }
+
+ @Test
+ public void validateAndroidSystem() {
+ assumeTrue(ApiLevelUtil.isAtLeast(Build.VERSION_CODES.Q));
+
+ Set<SplitPermissionInfo> seenSplits = new HashSet<>(6);
+
+ for (SplitPermissionInfo split : mSplitPermissions) {
+ String splitPermission = split.getSplitPermission();
+ boolean isAndroid = splitPermission.startsWith("android");
+
+ if (!isAndroid) {
+ continue;
+ }
+
+ assertThat(seenSplits).doesNotContain(split);
+ seenSplits.add(split);
+
+ List<String> newPermissions = split.getNewPermissions();
+
+ switch (splitPermission) {
+ case ACCESS_FINE_LOCATION:
+ // Q declares multiple for ACCESS_FINE_LOCATION, so assert both exist
+ if (newPermissions.contains(ACCESS_COARSE_LOCATION)) {
+ assertSplit(split, NO_TARGET, ACCESS_COARSE_LOCATION);
+ } else {
+ assertSplit(split, Build.VERSION_CODES.Q, ACCESS_BACKGROUND_LOCATION);
+ }
+ break;
+ case WRITE_EXTERNAL_STORAGE:
+ if (newPermissions.contains(READ_EXTERNAL_STORAGE)) {
+ assertSplit(split, NO_TARGET, READ_EXTERNAL_STORAGE);
+ } else if (newPermissions.contains(ACCESS_MEDIA_LOCATION)) {
+ assertSplit(split, Build.VERSION_CODES.Q, ACCESS_MEDIA_LOCATION);
+ } else if (newPermissions.contains(READ_MEDIA_AUDIO)) {
+ assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_AUDIO);
+ } else if (newPermissions.contains(READ_MEDIA_VIDEO)) {
+ assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_VIDEO);
+ } else if (newPermissions.contains(READ_MEDIA_IMAGES)) {
+ assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_IMAGES);
+ }
+ break;
+ case READ_CONTACTS:
+ assertSplit(split, Build.VERSION_CODES.JELLY_BEAN, READ_CALL_LOG);
+ break;
+ case WRITE_CONTACTS:
+ assertSplit(split, Build.VERSION_CODES.JELLY_BEAN, WRITE_CALL_LOG);
+ break;
+ case ACCESS_COARSE_LOCATION:
+ assertSplit(split, Build.VERSION_CODES.Q, ACCESS_BACKGROUND_LOCATION);
+ break;
+ case READ_EXTERNAL_STORAGE:
+ if (newPermissions.contains(ACCESS_MEDIA_LOCATION)) {
+ assertSplit(split, Build.VERSION_CODES.Q, ACCESS_MEDIA_LOCATION);
+ } else if (newPermissions.contains(READ_MEDIA_AUDIO)) {
+ assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_AUDIO);
+ } else if (newPermissions.contains(READ_MEDIA_VIDEO)) {
+ assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_VIDEO);
+ } else if (newPermissions.contains(READ_MEDIA_IMAGES)) {
+ assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_IMAGES);
+ }
+ break;
+ case READ_PRIVILEGED_PHONE_STATE:
+ assertSplit(split, NO_TARGET, READ_PHONE_STATE);
+ break;
+ case BLUETOOTH_CONNECT:
+ assertSplit(split, Build.VERSION_CODES.S, BLUETOOTH, BLUETOOTH_ADMIN);
+ break;
+ case BLUETOOTH_SCAN:
+ assertSplit(split, Build.VERSION_CODES.S, BLUETOOTH, BLUETOOTH_ADMIN);
+ break;
+ case BODY_SENSORS:
+ assertSplit(split, Build.VERSION_CODES.TIRAMISU, BODY_SENSORS_BACKGROUND);
+ break;
+ case ACCESS_MEDIA_LOCATION:
+ case READ_MEDIA_IMAGES:
+ case READ_MEDIA_VIDEO:
+ assertSplit(split, READ_MEDIA_VISUAL_USER_SELECTED);
+ break;
+ }
+ }
+
+ assertEquals(24, seenSplits.size());
+ }
+
+ private void assertSplit(SplitPermissionInfo split, int targetSdk, String... permission) {
+ assertThat(split.getNewPermissions()).containsExactlyElementsIn(permission);
+ assertThat(split.getTargetSdk()).isEqualTo(targetSdk);
+ }
+
+ private void assertSplit(SplitPermissionInfo split, String... permission) {
+ assertThat(split.getNewPermissions()).containsExactlyElementsIn(permission);
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/StorageEscalationTest.kt b/tests/cts/permission/src/android/permission/cts/StorageEscalationTest.kt
new file mode 100644
index 000000000..2458baeb2
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/StorageEscalationTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts
+
+import android.Manifest.permission.ACCESS_MEDIA_LOCATION
+import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import android.platform.test.annotations.AppModeFull
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeNoException
+import org.junit.Before
+import org.junit.Test
+
+@AppModeFull
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class StorageEscalationTest {
+ companion object {
+ private const val APK_DIRECTORY = "/data/local/tmp/cts-permission"
+ const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsStorageEscalationApp28.apk"
+ const val APP_APK_PATH_29_SCOPED = "$APK_DIRECTORY/CtsStorageEscalationApp29Scoped.apk"
+ const val APP_APK_PATH_29_FULL = "$APK_DIRECTORY/CtsStorageEscalationApp29Full.apk"
+ const val APP_PACKAGE_NAME = "android.permission.cts.storageescalation"
+ const val DELAY_TIME_MS: Long = 200
+ val permissions =
+ listOf<String>(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, ACCESS_MEDIA_LOCATION)
+ }
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.context
+ private val uiAutomation: UiAutomation = instrumentation.uiAutomation
+ private var secondaryUserId: Int? = null
+
+ @Before
+ @After
+ fun uninstallApp() {
+ SystemUtil.runShellCommand("pm uninstall $APP_PACKAGE_NAME --user ALL")
+ }
+
+ private fun installPackage(apk: String) {
+ var userString = ""
+ secondaryUserId?.let { userId -> userString = " --user $userId" }
+ val result = SystemUtil.runShellCommandOrThrow("pm install -r$userString $apk")
+ assertTrue(
+ "Expected output to contain \"Success\", but was \"$result\"",
+ result.contains("Success")
+ )
+ }
+
+ private fun createSecondaryUser() {
+ val createUserOutput: String = SystemUtil.runShellCommand("pm create-user secondary")
+ var formatException: Exception? = null
+ val userId =
+ try {
+ createUserOutput.split(" id ".toRegex())[1].trim { it <= ' ' }.toInt()
+ } catch (e: Exception) {
+ formatException = e
+ -1
+ }
+ assumeNoException("Failed to parse userId from $createUserOutput", formatException)
+ SystemUtil.runShellCommand("am start-user -w $userId")
+ secondaryUserId = userId
+ }
+
+ @After
+ fun removeSecondaryUser() {
+ secondaryUserId?.let { userId ->
+ SystemUtil.runShellCommand("pm remove-user $userId")
+ secondaryUserId = null
+ }
+ }
+
+ private fun grantStoragePermissions() {
+ for (permName in permissions) {
+ var user = Process.myUserHandle()
+ secondaryUserId?.let { user = UserHandle.of(it) }
+ uiAutomation.grantRuntimePermissionAsUser(APP_PACKAGE_NAME, permName, user)
+ }
+ }
+
+ private fun assertStoragePermissionState(granted: Boolean) {
+ for (permName in permissions) {
+ var userContext = context
+ secondaryUserId?.let { userId ->
+ SystemUtil.runWithShellPermissionIdentity {
+ userContext =
+ context.createPackageContextAsUser(
+ APP_PACKAGE_NAME,
+ 0,
+ UserHandle.of(userId)
+ )
+ }
+ }
+ Assert.assertEquals(
+ granted,
+ userContext.packageManager.checkPermission(permName, APP_PACKAGE_NAME) ==
+ PackageManager.PERMISSION_GRANTED
+ )
+ }
+ }
+
+ @Test
+ fun testCannotEscalateWithSdkDowngrade() {
+ runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_28)
+ }
+
+ @Test
+ fun testCannotEscalateWithNewManifestLegacyRequest() {
+ runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_29_FULL)
+ }
+
+ @Test
+ fun testCannotEscalateWithSdkDowngradeSecondary() {
+ createSecondaryUser()
+ runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_28)
+ }
+
+ @Test
+ fun testCannotEscalateWithNewManifestLegacyRequestSecondary() {
+ createSecondaryUser()
+ runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_29_FULL)
+ }
+
+ private fun runStorageEscalationTest(startPackageApk: String, finishPackageApk: String) {
+ installPackage(startPackageApk)
+ grantStoragePermissions()
+ assertStoragePermissionState(granted = true)
+ installPackage(finishPackageApk)
+ // permission revoke is async, so wait a short period
+ Thread.sleep(DELAY_TIME_MS)
+ assertStoragePermissionState(granted = false)
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/TvPermissionTest.java b/tests/cts/permission/src/android/permission/cts/TvPermissionTest.java
new file mode 100644
index 000000000..e61b2667d
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/TvPermissionTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.platform.test.annotations.AppModeFull;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for TV API related permissions.
+ */
+public class TvPermissionTest extends AndroidTestCase {
+ private static final String DUMMY_INPUT_ID = "dummy";
+
+ private boolean mHasTvInputFramework;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHasTvInputFramework = getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void verifyInsert(Uri uri, String tableName) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().insert(uri, values);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ } catch (IllegalArgumentException e) {
+ // TvProvider is not visable for instant app
+ }
+ }
+
+ public void verifyUpdate(Uri uri, String tableName) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().update(uri, values, null, null);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ } catch (IllegalArgumentException e) {
+ // TvProvider is not visable for instant app
+ }
+ }
+
+ public void verifyDelete(Uri uri, String tableName) throws Exception {
+ try {
+ getContext().getContentResolver().delete(uri, null, null);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ } catch (IllegalArgumentException e) {
+ // TvProvider is not visable for instant app
+ }
+ }
+
+ @AppModeFull
+ public void testInsertChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyInsert(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ @AppModeFull
+ public void testUpdateChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyUpdate(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ @AppModeFull
+ public void testDeleteChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyDelete(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ @AppModeFull
+ public void testInsertPrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyInsert(TvContract.Programs.CONTENT_URI, "programs");
+ }
+
+ @AppModeFull
+ public void testUpdatePrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyUpdate(TvContract.Programs.CONTENT_URI, "programs");
+ }
+
+ @AppModeFull
+ public void testDeletePrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyDelete(TvContract.Programs.CONTENT_URI, "programs");
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt b/tests/cts/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt
new file mode 100644
index 000000000..3e6b01b82
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+package android.permission.cts
+
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.RECORD_AUDIO
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Process
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.UiObjectNotFoundException
+import com.android.compatibility.common.util.FeatureUtil
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject
+import java.util.regex.Pattern
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests that the permissioncontroller behaves normally when an app defines a permission in the
+ * android.permission-group.UNDEFINED group
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+class UndefinedGroupPermissionTest {
+ private var mInstrumentation: Instrumentation? = null
+ private var mUiDevice: UiDevice? = null
+ private var mContext: Context? = null
+ private var mPm: PackageManager? = null
+ private var mAllowButtonText: Pattern? = null
+ private var mDenyButtonText: Pattern? = null
+
+ @Before
+ fun install() {
+ SystemUtil.runShellCommand("pm uninstall $APP_PKG_NAME")
+ SystemUtil.runShellCommandOrThrow(
+ "pm install -r " + TEST_APP_DEFINES_UNDEFINED_PERMISSION_GROUP_ELEMENT_APK
+ )
+ }
+
+ @Before
+ fun setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation()
+ mUiDevice = UiDevice.getInstance(mInstrumentation!!)
+ mContext = mInstrumentation?.targetContext
+ mPm = mContext?.packageManager
+ val permissionControllerResources =
+ mContext
+ ?.createPackageContext(mContext?.packageManager?.permissionControllerPackageName, 0)
+ ?.resources
+ mAllowButtonText =
+ Pattern.compile(
+ Pattern.quote(
+ requireNotNull(
+ permissionControllerResources?.getString(
+ permissionControllerResources.getIdentifier(
+ "grant_dialog_button_allow",
+ "string",
+ "com.android.permissioncontroller"
+ )
+ )
+ )
+ ),
+ Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE
+ )
+ mDenyButtonText =
+ Pattern.compile(
+ Pattern.quote(
+ requireNotNull(
+ permissionControllerResources?.getString(
+ permissionControllerResources.getIdentifier(
+ "grant_dialog_button_deny",
+ "string",
+ "com.android.permissioncontroller"
+ )
+ )
+ )
+ ),
+ Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE
+ )
+ }
+
+ @Before
+ fun wakeUpScreenAndUnlock() {
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP")
+ SystemUtil.runShellCommand("input keyevent KEYCODE_MENU")
+ }
+
+ @Test
+ fun testOtherGroupPermissionsNotGranted_1() {
+ testOtherGroupPermissionsNotGranted(CAMERA, RECORD_AUDIO)
+ }
+
+ @Test
+ fun testOtherGroupPermissionsNotGranted_2() {
+ testOtherGroupPermissionsNotGranted(TEST, RECORD_AUDIO)
+ }
+
+ @Test
+ fun testOtherGroupPermissionsNotGranted_3() {
+ testOtherGroupPermissionsNotGranted(CAMERA, TEST)
+ }
+
+ /** When the custom permission is granted nothing else gets granted as a byproduct. */
+ @Test
+ fun testCustomPermissionGrantedAlone() {
+ Assert.assertEquals(
+ PackageManager.PERMISSION_DENIED,
+ mPm!!.checkPermission(CAMERA, APP_PKG_NAME)
+ )
+ Assert.assertEquals(
+ PackageManager.PERMISSION_DENIED,
+ mPm!!.checkPermission(RECORD_AUDIO, APP_PKG_NAME)
+ )
+ Assert.assertEquals(
+ PackageManager.PERMISSION_DENIED,
+ mPm!!.checkPermission(TEST, APP_PKG_NAME)
+ )
+ eventually {
+ startRequestActivity(arrayOf(TEST))
+ mUiDevice!!.waitForIdle()
+ Thread.sleep(2000)
+ findAllowButton().click()
+ }
+ eventually {
+ Assert.assertEquals(
+ PackageManager.PERMISSION_DENIED,
+ mPm!!.checkPermission(CAMERA, APP_PKG_NAME)
+ )
+ Assert.assertEquals(
+ PackageManager.PERMISSION_DENIED,
+ mPm!!.checkPermission(RECORD_AUDIO, APP_PKG_NAME)
+ )
+ Assert.assertEquals(
+ PackageManager.PERMISSION_GRANTED,
+ mPm!!.checkPermission(TEST, APP_PKG_NAME)
+ )
+ }
+ }
+
+ @After
+ fun uninstall() {
+ SystemUtil.runShellCommand("pm uninstall $APP_PKG_NAME")
+ }
+
+ fun findAllowButton(): UiObject2 {
+ return if (FeatureUtil.isAutomotive() || FeatureUtil.isWatch()) {
+ waitFindObject(By.text(mAllowButtonText!!))
+ } else {
+ waitFindObject(
+ By.res("com.android.permissioncontroller:id/permission_allow_button"),
+ 2000
+ )
+ }
+ }
+
+ /**
+ * If app has one permission granted, then it can't grant itself another permission for free.
+ */
+ fun testOtherGroupPermissionsNotGranted(grantedPerm: String, targetPermission: String) {
+ // Grant the permission in the background
+ SystemUtil.runWithShellPermissionIdentity {
+ mPm!!.grantRuntimePermission(APP_PKG_NAME, grantedPerm, Process.myUserHandle())
+ }
+ Assert.assertEquals(
+ "$grantedPerm not granted.",
+ PackageManager.PERMISSION_GRANTED,
+ mPm!!.checkPermission(grantedPerm, APP_PKG_NAME)
+ )
+
+ // If the dialog shows, success. If not then either the UI is broken or the permission was
+ // granted in the background.
+ eventually {
+ startRequestActivity(arrayOf(targetPermission))
+ mUiDevice!!.waitForIdle()
+ try {
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isWatch()) {
+ waitFindObject(By.text(mDenyButtonText!!))
+ } else if (
+ mContext?.packageManager?.hasSystemFeature(PackageManager.FEATURE_WATCH) == true
+ ) {
+ waitFindObject(By.res(ALLOW_BUTTON), 2000)
+ } else {
+ waitFindObject(By.res(GRANT_DIALOG), 2000)
+ }
+ } catch (e: UiObjectNotFoundException) {
+ Assert.assertEquals(
+ "grant dialog never showed.",
+ PackageManager.PERMISSION_GRANTED,
+ mPm!!.checkPermission(targetPermission, APP_PKG_NAME)
+ )
+ }
+ }
+ Assert.assertEquals(
+ PackageManager.PERMISSION_DENIED,
+ mPm!!.checkPermission(targetPermission, APP_PKG_NAME)
+ )
+ }
+
+ private fun startRequestActivity(permissions: Array<String>) {
+ mContext!!.startActivity(
+ Intent()
+ .setComponent(ComponentName(APP_PKG_NAME, "$APP_PKG_NAME.RequestPermissions"))
+ .putExtra(EXTRA_PERMISSIONS, permissions)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ }
+
+ companion object {
+ private const val TEST_APP_DEFINES_UNDEFINED_PERMISSION_GROUP_ELEMENT_APK =
+ "/data/local/tmp/cts-permission/AppThatDefinesUndefinedPermissionGroupElement.apk"
+ private const val APP_PKG_NAME = "android.permission.cts.appthatrequestpermission"
+ private const val EXTRA_PERMISSIONS =
+ "android.permission.cts.appthatrequestpermission.extra.PERMISSIONS"
+ private const val GRANT_DIALOG = "com.android.permissioncontroller:id/grant_dialog"
+ private const val ALLOW_BUTTON =
+ "com.android.permissioncontroller:id/permission_allow_button"
+ const val TEST = "android.permission.cts.appthatrequestpermission.TEST"
+ }
+}
diff --git a/tests/cts/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl b/tests/cts/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
new file mode 100644
index 000000000..be92ed160
--- /dev/null
+++ b/tests/cts/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+package android.permission.cts.appthataccesseslocation;
+
+interface IAccessLocationOnCommand {
+ /** Access location on command */
+ void accessLocation();
+} \ No newline at end of file
diff --git a/tests/cts/permission/telephony/Android.bp b/tests/cts/permission/telephony/Android.bp
new file mode 100644
index 000000000..bbfe06c55
--- /dev/null
+++ b/tests/cts/permission/telephony/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2018 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: "CtsPermissionTestCasesTelephony",
+ defaults: ["cts_defaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ ],
+ // Include both the 32 and 64 bit versions
+ compile_multilib: "both",
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ ],
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+}
diff --git a/tests/cts/permission/telephony/AndroidManifest.xml b/tests/cts/permission/telephony/AndroidManifest.xml
new file mode 100644
index 000000000..0349880e2
--- /dev/null
+++ b/tests/cts/permission/telephony/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.permission.cts.telephony" android:targetSandboxVersion="2">
+
+ <!--
+ The CTS stubs package cannot be used as the target application here,
+ since that requires many permissions to be set. Instead, specify this
+ package itself as the target and include any stub activities needed.
+
+ This test package uses the default InstrumentationTestRunner, because
+ the InstrumentationCtsTestRunner is only available in the stubs
+ package. That runner cannot be added to this package either, since it
+ relies on hidden APIs.
+ -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permission.cts.telephony"
+ android:label="CTS tests of android.permission">
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/cts/permission/telephony/AndroidTest.xml b/tests/cts/permission/telephony/AndroidTest.xml
new file mode 100644
index 000000000..13ee78bcc
--- /dev/null
+++ b/tests/cts/permission/telephony/AndroidTest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Telephony Permission test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
+ <!-- Telephony permission tests do not use any permission not available to instant apps. -->
+ <option name="config-descriptor:metadata" key="parameter" value="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="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+
+ <!-- Disable package verifier before installing APKs -->
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ <option name="force-skip-system-props" value="true" />
+ </target_preparer>
+
+ <!-- Install main test suite apk -->
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionTestCasesTelephony.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permission.cts.telephony" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/cts/permission/telephony/OWNERS b/tests/cts/permission/telephony/OWNERS
new file mode 100644
index 000000000..446cf1a00
--- /dev/null
+++ b/tests/cts/permission/telephony/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+# Bug component: 20868
+include platform/cts:/tests/tests/telephony/OWNERS \ No newline at end of file
diff --git a/tests/cts/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java b/tests/cts/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
new file mode 100644
index 000000000..3605be21a
--- /dev/null
+++ b/tests/cts/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.telephony;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Test the non-location-related functionality of TelephonyManager.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TelephonyManagerPermissionTest {
+
+ private boolean mHasTelephony;
+ TelephonyManager mTelephonyManager = null;
+ private AudioManager mAudioManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mHasTelephony = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY);
+ mTelephonyManager =
+ (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ assertNotNull(mTelephonyManager);
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull(mAudioManager);
+ }
+
+ /**
+ * Verify that TelephonyManager.getDeviceId requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetDeviceId() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String id = mTelephonyManager.getDeviceId();
+ fail("Got device ID: " + id);
+ } catch (SecurityException e) {
+ // expected
+ }
+ try {
+ String id = mTelephonyManager.getDeviceId(0);
+ fail("Got device ID: " + id);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that TelephonyManager.getLine1Number requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetLine1Number() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String nmbr = mTelephonyManager.getLine1Number();
+ fail("Got line 1 number: " + nmbr);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that TelephonyManager.getSimSerialNumber requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetSimSerialNumber() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String nmbr = mTelephonyManager.getSimSerialNumber();
+ fail("Got SIM serial number: " + nmbr);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that TelephonyManager.getSubscriberId requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetSubscriberId() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String sid = mTelephonyManager.getSubscriberId();
+ fail("Got subscriber id: " + sid);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that TelephonyManager.getVoiceMailNumber requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testVoiceMailNumber() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String vmnum = mTelephonyManager.getVoiceMailNumber();
+ fail("Got voicemail number: " + vmnum);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+ /**
+ * Verify that AudioManager.setMode requires Permission.
+ * <p>
+ * Requires Permissions:
+ * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} and
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE} for
+ * {@link AudioManager#MODE_IN_CALL}.
+ */
+ @Test
+ public void testSetMode() {
+ if (!mHasTelephony) {
+ return;
+ }
+ int audioMode = mAudioManager.getMode();
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ assertEquals(audioMode, mAudioManager.getMode());
+ }
+
+ /**
+ * Tests that isManualNetworkSelectionAllowed requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testIsManualNetworkSelectionAllowedWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ mTelephonyManager.isManualNetworkSelectionAllowed();
+ fail("Expected SecurityException. App does not have carrier privileges.");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests that getManualNetworkSelectionPlmn requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testGetManualNetworkSelectionPlmnWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ mTelephonyManager.getManualNetworkSelectionPlmn();
+ fail("Expected SecurityException. App does not have carrier privileges.");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Verify that Telephony related broadcasts are protected.
+ */
+ @Test
+ public void testProtectedBroadcasts() {
+ if (!mHasTelephony) {
+ return;
+ }
+ try {
+ Intent intent = new Intent("android.intent.action.SIM_STATE_CHANGED");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent("android.intent.action.SERVICE_STATE");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent("android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent(
+ "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent(
+ "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent(
+ "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent("android.intent.action.SIG_STR");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ try {
+ Intent intent = new Intent("android.provider.Telephony.SECRET_CODE");
+ getContext().sendBroadcast(intent);
+ fail("SecurityException expected!");
+ } catch (SecurityException e) {}
+ }
+
+ /**
+ * Verify that TelephonyManager.getImei requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetImei() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String imei = mTelephonyManager.getImei();
+ fail("Got IMEI: " + imei);
+ } catch (SecurityException e) {
+ // expected
+ }
+ try {
+ String imei = mTelephonyManager.getImei(0);
+ fail("Got IMEI: " + imei);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that getNetworkType and getDataNetworkType requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void testGetNetworkType() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ mTelephonyManager.getNetworkType();
+ fail("getNetworkType did not throw a SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mTelephonyManager.getDataNetworkType();
+ fail("getDataNetworkType did not throw a SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Tests that getNetworkSelectionMode requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testGetNetworkSelectionModeWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ assertThrowsSecurityException(() -> mTelephonyManager.getNetworkSelectionMode(),
+ "Expected SecurityException. App does not have carrier privileges.");
+ }
+
+ /**
+ * Tests that setNetworkSelectionModeAutomatic requires permission
+ * Expects a security exception since the caller does not have carrier privileges.
+ */
+ @Test
+ public void testSetNetworkSelectionModeAutomaticWithoutPermission() {
+ if (!mHasTelephony) {
+ return;
+ }
+ assertThrowsSecurityException(() -> mTelephonyManager.setNetworkSelectionModeAutomatic(),
+ "Expected SecurityException. App does not have carrier privileges.");
+ }
+
+ /**
+ * Verify that setForbiddenPlmns requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ */
+ @Test
+ public void testSetForbiddenPlmns() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
+ fail("SetForbiddenPlmns did not throw a SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ static final int PHONE_STATE_PERMISSION_MASK =
+ PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+ | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
+
+ static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
+ PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+ | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
+ | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
+ | PhoneStateListener.LISTEN_BARRING_INFO;
+
+ static final int PHONE_PERMISSIONS_MASK =
+ PHONE_STATE_PERMISSION_MASK | PRECISE_PHONE_STATE_PERMISSION_MASK;
+
+ /**
+ * Verify the documented permissions for PhoneStateListener.
+ */
+ @Test
+ public void testListen() {
+ PhoneStateListener psl = new PhoneStateListener((Runnable r) -> { });
+
+ try {
+ for (int i = 1; i != 0; i = i << 1) {
+ if ((i & PHONE_PERMISSIONS_MASK) == 0) continue;
+ final int listenBit = i;
+ assertThrowsSecurityException(() -> mTelephonyManager.listen(psl, listenBit),
+ "Expected a security exception for " + Integer.toHexString(i));
+ }
+ } finally {
+ mTelephonyManager.listen(psl, PhoneStateListener.LISTEN_NONE);
+ }
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ // An actual version of assertThrows() was added in JUnit5
+ private static <T extends Throwable> void assertThrows(Class<T> clazz, Runnable r,
+ String message) {
+ try {
+ r.run();
+ } catch (Exception expected) {
+ assertTrue(clazz.isAssignableFrom(expected.getClass()));
+ return;
+ }
+ fail(message);
+ }
+
+ private static void assertThrowsSecurityException(Runnable r, String message) {
+ assertThrows(SecurityException.class, r, message);
+ }
+
+ /**
+ * Verify that TelephonyManager.getPrimaryImei requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}.
+ */
+ @Test
+ public void testGetPrimaryImei() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ String primaryImei = mTelephonyManager.getPrimaryImei();
+ fail("Received Primary Imei value: " + primaryImei);
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that getCarrierRestrictionStatus requires permission
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE}.
+ */
+ @Test
+ public void getCarrierRestrictionStatus_Exception() {
+ if (!mHasTelephony) {
+ return;
+ }
+ LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
+ try {
+ mTelephonyManager.getCarrierRestrictionStatus(getContext().getMainExecutor(),
+ carrierRestrictionStatusResult::offer);
+ // Test case fail, if the API don't catch the security exception.
+ fail();
+ } catch (SecurityException ex) {
+ // expecting the security exception.
+ } catch (Exception ex) {
+ // The test case should fail if other than security exception comes.
+ fail();
+ }
+ }
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp
new file mode 100644
index 000000000..6f0bcf111
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAdversarialPermissionDefinerApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ certificate: ":cts-testkey1",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 000000000..d28fba87c
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.AdversarialPermissionDefinerApp">
+
+ <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission"
+ android:protectionLevel="dangerous"
+ android:label="TestPermission"
+ android:description="@string/test_permission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
new file mode 100644
index 000000000..bfb3e1e2b
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="test_permission">Test Permission</string>
+</resources>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp
new file mode 100644
index 000000000..41f01e981
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsAdversarialPermissionUserApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
new file mode 100644
index 000000000..f514d549a
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.userapp">
+
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
new file mode 100644
index 000000000..a3e1f13d2
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsInstallPermissionDefinerApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ certificate: ":cts-testkey1",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 000000000..2df67431b
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.installpermissiondefinerapp">
+
+ <permission
+ android:name="android.permission.cts.revokepermissionwhenremoved.TestInstallPermission"
+ android:protectionLevel="normal" />
+
+ <application />
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
new file mode 100644
index 000000000..caaa8e898
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsInstallPermissionEscalatorApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ certificate: ":cts-testkey1",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.xml
new file mode 100644
index 000000000..6320618c5
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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="android.permission.cts.revokepermissionwhenremoved.installpermissionescalatorapp">
+
+ <permission
+ android:name="android.permission.cts.revokepermissionwhenremoved.TestInstallPermission"
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="dangerous" />
+
+ <application />
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
new file mode 100644
index 000000000..d9ca4c8ea
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsInstallPermissionUserApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml
new file mode 100644
index 000000000..acfafa986
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.installpermissionuserapp">
+
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestInstallPermission" />
+
+ <application />
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml
new file mode 100644
index 000000000..7a0e40554
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/InstalltimePermissionUserApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.installtimepermissionuserapp">
+
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestInstalltimePermission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp
new file mode 100644
index 000000000..b4ea30ada
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsRuntimePermissionDefinerApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ certificate: ":cts-testkey1",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 000000000..d3cf6d0aa
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp">
+
+ <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission"
+ android:protectionLevel="dangerous"
+ android:label="TestPermission"
+ android:description="@string/test_permission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
new file mode 100644
index 000000000..bfb3e1e2b
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="test_permission">Test Permission</string>
+</resources>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp
new file mode 100644
index 000000000..22c6ccd6f
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsRuntimePermissionUserApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "sts",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
new file mode 100644
index 000000000..d977e46e4
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp">
+
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp
new file mode 100644
index 000000000..5627a3d36
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsVictimPermissionDefinerApp",
+ defaults: ["cts_defaults"],
+ sdk_version: "current",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ "sts",
+ "mts-permission",
+ ],
+ certificate: ":cts-testkey1",
+}
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 000000000..3fb0abd29
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 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.permission.cts.revokepermissionwhenremoved.VictimPermissionDefinerApp">
+ <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission"
+ android:protectionLevel="signature"
+ android:label="Test Permission"
+ android:description="@string/test_permission" />
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
new file mode 100644
index 000000000..bfb3e1e2b
--- /dev/null
+++ b/tests/cts/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="test_permission">Test Permission</string>
+</resources>
diff --git a/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/Android.bp b/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/Android.bp
new file mode 100644
index 000000000..29647c50c
--- /dev/null
+++ b/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAccessRemoteDeviceCamera",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ "permission-multidevice-test-util-lib",
+ ],
+ min_sdk_version: "34",
+ target_sdk_version: "35",
+}
diff --git a/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/AndroidManifest.xml b/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/AndroidManifest.xml
new file mode 100644
index 000000000..211e415bd
--- /dev/null
+++ b/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionmultidevice.cts.accessremotedevicecamera">
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <application>
+ <activity android:name=".RequestPermissionActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/src/android/permissionmultidevice/cts/accessremotedevicecamera/RequestPermissionActivity.kt b/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/src/android/permissionmultidevice/cts/accessremotedevicecamera/RequestPermissionActivity.kt
new file mode 100644
index 000000000..fa1d1f83c
--- /dev/null
+++ b/tests/cts/permissionmultidevice/AccessRemoteDeviceCameraApp/src/android/permissionmultidevice/cts/accessremotedevicecamera/RequestPermissionActivity.kt
@@ -0,0 +1,64 @@
+/*
+ * 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 android.permissionmultidevice.cts.accessremotedevicecamera
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.RemoteCallback
+import android.permissionmultidevice.cts.TestConstants
+
+class RequestPermissionActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val deviceId =
+ intent.getIntExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID,
+ Context.DEVICE_ID_DEFAULT
+ )
+
+ requestPermissions(DEVICE_AWARE_PERMISSIONS, 1001, deviceId)
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray,
+ deviceId: Int
+ ) {
+ val resultReceiver =
+ intent.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, RemoteCallback::class.java)
+ val result =
+ Bundle().apply {
+ putStringArray(TestConstants.PERMISSION_RESULT_KEY_PERMISSIONS, permissions)
+ putIntArray(TestConstants.PERMISSION_RESULT_KEY_GRANT_RESULTS, grantResults)
+ putInt(TestConstants.PERMISSION_RESULT_KEY_DEVICE_ID, deviceId)
+ }
+
+ resultReceiver?.sendResult(result)
+ finish()
+ }
+
+ companion object {
+ private val DEVICE_AWARE_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
+ }
+}
diff --git a/tests/cts/permissionmultidevice/Android.bp b/tests/cts/permissionmultidevice/Android.bp
new file mode 100644
index 000000000..fc4c1ddaf
--- /dev/null
+++ b/tests/cts/permissionmultidevice/Android.bp
@@ -0,0 +1,52 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsPermissionMultiDeviceTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "modules-utils-build_system",
+ "cts-wm-util",
+ "flag-junit",
+ "android.companion.virtual.flags-aconfig-java",
+ "permission-test-util-lib",
+ "permission-multidevice-test-util-lib",
+ "android.permission.flags-aconfig-java-export",
+ "collector-device-lib",
+ ],
+ data: [
+ ":CtsAccessRemoteDeviceCamera",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "mcts-permission",
+ ],
+}
diff --git a/tests/cts/permissionmultidevice/AndroidManifest.xml b/tests/cts/permissionmultidevice/AndroidManifest.xml
new file mode 100644
index 000000000..9bad85813
--- /dev/null
+++ b/tests/cts/permissionmultidevice/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionmultidevice.cts">
+
+ <uses-feature android:name="android.software.companion_device_setup" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permissionmultidevice.cts"
+ android:label="CTS permission multi-device tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/cts/permissionmultidevice/AndroidTest.xml b/tests/cts/permissionmultidevice/AndroidTest.xml
new file mode 100644
index 000000000..fe70d23ec
--- /dev/null
+++ b/tests/cts/permissionmultidevice/AndroidTest.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<configuration description="Config for CTS permission multi-device test cases">
+
+ <option name="test-suite-tag" value="cts" />
+
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk34ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ <option name="screen-always-on" value="on" />
+ <option name="disable-device-config-sync" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionMultiDeviceTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="CtsAccessRemoteDeviceCamera.apk->/data/local/tmp/cts-permissionmultidevice/CtsAccessRemoteDeviceCamera.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="appops set android.permissionmultidevice.cts REQUEST_INSTALL_PACKAGES allow" />
+ <!-- disable DeprecatedAbi warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <!-- disable DeprecatedTargetSdk warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1" />
+ <!-- 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" />
+ <!-- Dismiss any system dialogs (e.g. crashes, ANR). -->
+ <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS --receiver-foreground" />
+ <!-- Ensure consistent Intent launch by overriding device provision and user setup status -->
+ <option name="run-command" value="settings put global device_provisioned 1" />
+ <option name="run-command" value="settings put secure user_setup_complete 1" />
+ </target_preparer>
+
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-permissionmultidevice" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionmultidevice"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permissionmultidevice.cts" />
+ <option name="runtime-hint" value="5m" />
+ <option name="device-listeners" value="android.device.collectors.ScreenshotOnFailureCollector"/>
+ </test>
+
+ <!-- Collect the dumped files for debugging -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="view_.*" />
+ <!-- Collect the files generated on error -->
+ <option name="pull-pattern-keys" value="android.device.collectors.ScreenshotOnFailureCollector.*\.png"/>
+ <option name="directory-keys" value="/data/user/0/android.permissionmultidevice.cts/files" />
+ <option name="collect-on-run-ended-only" value="false" />
+ </metrics_collector>
+
+</configuration>
diff --git a/tests/cts/permissionmultidevice/OWNERS b/tests/cts/permissionmultidevice/OWNERS
new file mode 100644
index 000000000..fb6099cf7
--- /dev/null
+++ b/tests/cts/permissionmultidevice/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/cts/permissionmultidevice/TEST_MAPPING b/tests/cts/permissionmultidevice/TEST_MAPPING
new file mode 100644
index 000000000..80a2cf2ce
--- /dev/null
+++ b/tests/cts/permissionmultidevice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPermissionMultiDeviceTestCases"
+ }
+ ]
+}
diff --git a/tests/cts/permissionmultidevice/TestUtils/Android.bp b/tests/cts/permissionmultidevice/TestUtils/Android.bp
new file mode 100644
index 000000000..aeef5d134
--- /dev/null
+++ b/tests/cts/permissionmultidevice/TestUtils/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "permission-multidevice-test-util-lib",
+ sdk_version: "test_current",
+ min_sdk_version: "34",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "compatibility-device-util-axt",
+ "kotlinx-coroutines-android",
+ "CtsVirtualDeviceCommonLib",
+ ],
+ apex_available: [
+ "com.android.permission",
+ "test_com.android.permission",
+ ],
+ installable: false,
+ visibility: [
+ "//packages/modules/Permission:__subpackages__",
+ ],
+}
diff --git a/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PackageManagementUtils.kt b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PackageManagementUtils.kt
new file mode 100644
index 000000000..14885066c
--- /dev/null
+++ b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PackageManagementUtils.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionmultidevice.cts
+
+import com.android.compatibility.common.util.SystemUtil
+import com.android.modules.utils.build.SdkLevel
+import org.junit.Assert
+
+object PackageManagementUtils {
+ fun installPackage(
+ apkPath: String,
+ reinstall: Boolean = false,
+ grantRuntimePermissions: Boolean = false,
+ expectSuccess: Boolean = true,
+ installSource: String? = null
+ ) {
+ val output =
+ SystemUtil.runShellCommandOrThrow(
+ "pm install${if (SdkLevel.isAtLeastU()) " --bypass-low-target-sdk-block" else ""} " +
+ "${if (reinstall) " -r" else ""}${
+ if (grantRuntimePermissions) " -g"
+ else ""
+ }${if (installSource != null) " -i $installSource" else ""} $apkPath"
+ )
+ .trim()
+
+ if (expectSuccess) {
+ Assert.assertEquals("Success", output)
+ } else {
+ Assert.assertNotEquals("Success", output)
+ }
+ }
+
+ fun uninstallPackage(packageName: String, requireSuccess: Boolean = true) {
+ val output = SystemUtil.runShellCommand("pm uninstall $packageName").trim()
+ if (requireSuccess) {
+ Assert.assertEquals("Success", output)
+ }
+ }
+}
diff --git a/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PermissionUtils.kt b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PermissionUtils.kt
new file mode 100644
index 000000000..8a6290212
--- /dev/null
+++ b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/PermissionUtils.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionmultidevice.cts
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.util.Log
+
+object PermissionUtils {
+ private val TAG = PermissionUtils::class.java.getSimpleName()
+
+ fun isAutomotive(context: Context): Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+
+ fun isTv(context: Context): Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+
+ fun isWatch(context: Context): Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+
+ /**
+ * This method checks for the minimum screen size described in CDD {@see
+ * https://source.android.com/docs/compatibility/14/android-14-cdd#7111_screen_size_and_shape}
+ */
+ fun isCddCompliantScreenSize(): Boolean {
+ if (
+ Resources.getSystem().configuration.uiMode and Configuration.UI_MODE_TYPE_MASK ==
+ Configuration.UI_MODE_TYPE_WATCH
+ ) {
+ Log.d(TAG, "UI mode is UI_MODE_TYPE_WATCH, skipping the min dp check")
+ return true
+ }
+
+ val screenSize =
+ Resources.getSystem().configuration.screenLayout and
+ Configuration.SCREENLAYOUT_SIZE_MASK
+ return when (screenSize) {
+ Configuration.SCREENLAYOUT_SIZE_SMALL -> hasMinScreenSize(426, 320)
+ Configuration.SCREENLAYOUT_SIZE_NORMAL -> hasMinScreenSize(480, 320)
+ Configuration.SCREENLAYOUT_SIZE_LARGE -> hasMinScreenSize(640, 480)
+ Configuration.SCREENLAYOUT_SIZE_XLARGE -> hasMinScreenSize(960, 720)
+ else -> {
+ Log.e(TAG, "Unknown screen size: $screenSize")
+ true
+ }
+ }
+ }
+
+ private fun hasMinScreenSize(minWidthDp: Int, minHeightDp: Int): Boolean {
+ val dpi = Resources.getSystem().displayMetrics.densityDpi
+ val widthDp = (160f / dpi) * Resources.getSystem().displayMetrics.widthPixels
+ val heightDp = (160f / dpi) * Resources.getSystem().displayMetrics.heightPixels
+
+ // CDD does seem to follow width & height convention correctly, hence checking both ways
+ return (widthDp >= minWidthDp && heightDp >= minHeightDp) ||
+ (widthDp >= minHeightDp && heightDp >= minWidthDp)
+ }
+}
diff --git a/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/TestConstants.kt b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/TestConstants.kt
new file mode 100644
index 000000000..bdda5e045
--- /dev/null
+++ b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/TestConstants.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionmultidevice.cts
+
+object TestConstants {
+ const val PERMISSION_RESULT_KEY_PERMISSIONS = "permissions"
+ const val PERMISSION_RESULT_KEY_GRANT_RESULTS = "grantResults"
+ const val PERMISSION_RESULT_KEY_DEVICE_ID = "deviceId"
+}
diff --git a/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/UiAutomatorUtils.kt b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/UiAutomatorUtils.kt
new file mode 100644
index 000000000..448bf8905
--- /dev/null
+++ b/tests/cts/permissionmultidevice/TestUtils/src/android/permissionmultidevice/cts/UiAutomatorUtils.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionmultidevice.cts
+
+import android.os.SystemClock
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.StaleObjectException
+import androidx.test.uiautomator.UiObject2
+import com.android.compatibility.common.util.UiAutomatorUtils2
+import com.google.common.truth.Truth
+
+object UiAutomatorUtils {
+ fun waitFindObject(selector: BySelector): UiObject2 {
+ return findObjectWithRetry({ t -> UiAutomatorUtils2.waitFindObject(selector, t) })!!
+ }
+
+ fun waitFindObject(selector: BySelector, timeoutMillis: Long): UiObject2 {
+ return findObjectWithRetry(
+ { t -> UiAutomatorUtils2.waitFindObject(selector, t) },
+ timeoutMillis
+ )!!
+ }
+
+ fun click(selector: BySelector, timeoutMillis: Long = 20_000) {
+ waitFindObject(selector, timeoutMillis).click()
+ }
+
+ fun findTextForView(selector: BySelector): String {
+ val timeoutMs = 10000L
+
+ var exception: Exception? = null
+ var view: UiObject2? = null
+ try {
+ view = waitFindObject(selector, timeoutMs)
+ } catch (e: Exception) {
+ exception = e
+ }
+ Truth.assertThat(exception).isNull()
+ Truth.assertThat(view).isNotNull()
+ return view!!.text
+ }
+
+ private fun findObjectWithRetry(
+ automatorMethod: (timeoutMillis: Long) -> UiObject2?,
+ timeoutMillis: Long = 20_000L
+ ): UiObject2? {
+ val startTime = SystemClock.elapsedRealtime()
+ return try {
+ automatorMethod(timeoutMillis)
+ } catch (e: StaleObjectException) {
+ val remainingTime = timeoutMillis - (SystemClock.elapsedRealtime() - startTime)
+ if (remainingTime <= 0) {
+ throw e
+ }
+ automatorMethod(remainingTime)
+ }
+ }
+}
diff --git a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt
new file mode 100644
index 000000000..7ecc51ded
--- /dev/null
+++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/AppPermissionsTest.kt
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionmultidevice.cts
+
+import android.Manifest
+import android.app.Instrumentation
+import android.companion.virtual.VirtualDeviceManager
+import android.companion.virtual.VirtualDeviceParams
+import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM
+import android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.permission.PermissionManager
+import android.permission.flags.Flags
+import android.permissionmultidevice.cts.PermissionUtils.isCddCompliantScreenSize
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.virtualdevice.cts.common.VirtualDeviceRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiScrollable
+import androidx.test.uiautomator.UiSelector
+import androidx.test.uiautomator.Until
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.UiAutomatorUtils2
+import com.android.modules.utils.build.SdkLevel
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+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
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+class AppPermissionsTest {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val defaultDeviceContext = instrumentation.targetContext
+
+ @get:Rule
+ var virtualDeviceRule =
+ VirtualDeviceRule.withAdditionalPermissions(
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+ Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+ Manifest.permission.CREATE_VIRTUAL_DEVICE
+ )
+
+ private lateinit var persistentDeviceId: String
+ private lateinit var externalDeviceCameraText: String
+ private lateinit var permissionMessage: String
+
+ private val permissionManager =
+ defaultDeviceContext.getSystemService(PermissionManager::class.java)!!
+
+ private val TAG = AppPermissionsTest::class.java.simpleName
+
+ @Before
+ fun setup() {
+ assumeTrue(SdkLevel.isAtLeastV())
+ assumeFalse(PermissionUtils.isAutomotive(defaultDeviceContext))
+ assumeFalse(PermissionUtils.isTv(defaultDeviceContext))
+ assumeFalse(PermissionUtils.isWatch(defaultDeviceContext))
+ assumeTrue(isCddCompliantScreenSize())
+
+ PackageManagementUtils.installPackage(APP_APK_PATH_STREAMING)
+
+ val virtualDeviceManager =
+ defaultDeviceContext.getSystemService(VirtualDeviceManager::class.java)!!
+ val virtualDevice =
+ virtualDeviceRule.createManagedVirtualDevice(
+ VirtualDeviceParams.Builder()
+ .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_CUSTOM)
+ .build()
+ )
+
+ val mDeviceDisplayName =
+ virtualDeviceManager.getVirtualDevice(virtualDevice.deviceId)!!.displayName.toString()
+
+ persistentDeviceId = virtualDevice.persistentDeviceId!!
+ externalDeviceCameraText = "Camera on $mDeviceDisplayName"
+ permissionMessage = "Camera access for this app on $mDeviceDisplayName"
+ }
+
+ @After
+ fun cleanup() {
+ PackageManagementUtils.uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false)
+ UiAutomatorUtils2.getUiDevice().pressHome()
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun externalDevicePermissionGrantTest() {
+ grantRunTimePermission()
+
+ openAppPermissionsScreen()
+
+ clickPermissionItem(externalDeviceCameraText)
+
+ verifyPermissionMessage()
+
+ verifyRadioButtonStates(
+ allowForegroundChecked = true,
+ askChecked = false,
+ denyChecked = false
+ )
+
+ UiAutomatorUtils2.getUiDevice().pressBack()
+ val expectedGrantInfoMap =
+ mapOf(
+ "Allowed" to listOf(externalDeviceCameraText),
+ "Ask every time" to emptyList(),
+ "Not allowed" to listOf("Camera")
+ )
+ assertEquals(expectedGrantInfoMap, getGrantInfoMap())
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun externalDevicePermissionChangeToAskTest() {
+ grantRunTimePermission()
+ openAppPermissionsScreen()
+
+ clickPermissionItem(externalDeviceCameraText)
+ clickAskButton()
+
+ verifyAskSelection()
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun externalDevicePermissionChangeToDenyTest() {
+ grantRunTimePermission()
+ openAppPermissionsScreen()
+
+ clickPermissionItem(externalDeviceCameraText)
+ clickDenyButton()
+
+ verifyDenySelection()
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun externalDevicePermissionChangeToAllowTest() {
+ grantRunTimePermission()
+ openAppPermissionsScreen()
+
+ clickPermissionItem(externalDeviceCameraText)
+ clickAskButton()
+ verifyRadioButtonStates(
+ allowForegroundChecked = false,
+ askChecked = true,
+ denyChecked = false
+ )
+
+ clickAllowForegroundButton()
+ verifyAllowedSelection()
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun externalDevicePermissionNotDisplayedInitiallyTest() {
+ openAppPermissionsScreen()
+
+ // External device permission does not show initially (until requested)
+ val expectedGrantInfoMap =
+ mapOf(
+ "Allowed" to emptyList(),
+ "Ask every time" to emptyList(),
+ "Not allowed" to listOf("Camera")
+ )
+ assertEquals(expectedGrantInfoMap, getGrantInfoMap())
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun externalDevicePermissionStickyOnGrantTest() {
+ grantRunTimePermission()
+ openAppPermissionsScreen()
+
+ clickPermissionItem(externalDeviceCameraText)
+
+ verifyRadioButtonStates(
+ allowForegroundChecked = true,
+ askChecked = false,
+ denyChecked = false
+ )
+
+ clickDenyButton()
+
+ UiAutomatorUtils2.getUiDevice().pressBack()
+
+ // Verify the permission continue to show (sticky) after revoking, keeps option for users
+ // to change in future
+ val expectedGrantInfoMap =
+ mapOf(
+ "Allowed" to emptyList(),
+ "Ask every time" to emptyList(),
+ "Not allowed" to listOf("Camera", externalDeviceCameraText)
+ )
+ assertEquals(expectedGrantInfoMap, getGrantInfoMap())
+ }
+
+ private fun verifyAskSelection() {
+ verifyPermissionMessage()
+
+ verifyRadioButtonStates(
+ allowForegroundChecked = false,
+ askChecked = true,
+ denyChecked = false
+ )
+
+ UiAutomatorUtils2.getUiDevice().pressBack()
+
+ val expectedGrantInfoMap =
+ mapOf(
+ "Allowed" to emptyList(),
+ "Ask every time" to listOf(externalDeviceCameraText),
+ "Not allowed" to listOf("Camera")
+ )
+ assertEquals(expectedGrantInfoMap, getGrantInfoMap())
+
+ val permState = getPermState()
+ assertEquals(false, permState[DEVICE_AWARE_PERMISSION]!!.isGranted)
+ assertTrue(
+ permState[DEVICE_AWARE_PERMISSION]!!.flags and
+ PackageManager.FLAG_PERMISSION_ONE_TIME != 0
+ )
+ }
+
+ private fun verifyDenySelection() {
+ verifyPermissionMessage()
+
+ verifyRadioButtonStates(
+ allowForegroundChecked = false,
+ askChecked = false,
+ denyChecked = true
+ )
+
+ UiAutomatorUtils2.getUiDevice().pressBack()
+
+ val expectedGrantInfoMap =
+ mapOf(
+ "Allowed" to emptyList(),
+ "Ask every time" to emptyList(),
+ "Not allowed" to listOf("Camera", externalDeviceCameraText)
+ )
+ assertEquals(expectedGrantInfoMap, getGrantInfoMap())
+
+ val permState = getPermState()
+ assertEquals(false, permState[DEVICE_AWARE_PERMISSION]!!.isGranted)
+ assertTrue(
+ permState[DEVICE_AWARE_PERMISSION]!!.flags and
+ PackageManager.FLAG_PERMISSION_USER_SET != 0
+ )
+ }
+
+ private fun verifyAllowedSelection() {
+ verifyPermissionMessage()
+
+ verifyRadioButtonStates(
+ allowForegroundChecked = true,
+ askChecked = false,
+ denyChecked = false
+ )
+
+ UiAutomatorUtils2.getUiDevice().pressBack()
+
+ val expectedGrantInfoMap =
+ mapOf(
+ "Allowed" to listOf(externalDeviceCameraText),
+ "Ask every time" to emptyList(),
+ "Not allowed" to listOf("Camera")
+ )
+ assertEquals(expectedGrantInfoMap, getGrantInfoMap())
+
+ val permState = getPermState()
+ assertEquals(true, permState[DEVICE_AWARE_PERMISSION]!!.isGranted)
+ assertTrue(
+ permState[DEVICE_AWARE_PERMISSION]!!.flags and
+ PackageManager.FLAG_PERMISSION_USER_SET != 0
+ )
+ }
+
+ private fun verifyPermissionMessage() {
+ val actualText = UiAutomatorUtils2.waitFindObject(By.res(PERMISSION_MESSAGE_ID)).text
+ assertEquals(permissionMessage, actualText)
+ }
+
+ private fun getGrantInfoMap(): Map<String, List<String>> {
+ val grantInfoMap =
+ mapOf(
+ "Allowed" to mutableListOf<String>(),
+ "Ask every time" to mutableListOf(),
+ "Not allowed" to mutableListOf()
+ )
+ val outOfScopeTitles = setOf("Unused app settings", "Manage app if unused")
+
+ val titleSelector = UiSelector().resourceId(TITLE)
+ var currentGrantText = ""
+
+ val scrollable = getScrollableRecyclerView()
+
+ // Scrolling to end inorder to have the scrollable object loaded with all child element data
+ // ready to be read. If the scroll happens in the middle of the reading process, it has been
+ // observed that child items will be skipped during the reading (could be a bug). Hence this
+ // solution is to scroll to the bottom in the beginning and be more efficient as well.
+ scrollable.scrollToEnd(1)
+
+ for (i in 0..scrollable.childCount) {
+ val child = scrollable.getChild(UiSelector().index(i))
+ val titleText = child.getChild(titleSelector).text
+ if (outOfScopeTitles.contains(titleText)) {
+ break
+ }
+ if (grantInfoMap.contains(titleText)) {
+ currentGrantText = titleText
+ } else if (!titleText.startsWith("No permissions")) {
+ grantInfoMap[currentGrantText]!!.add(titleText)
+ }
+ }
+ return grantInfoMap
+ }
+
+ private fun verifyRadioButtonStates(
+ allowForegroundChecked: Boolean,
+ askChecked: Boolean,
+ denyChecked: Boolean
+ ) {
+ eventually {
+ assertEquals(
+ allowForegroundChecked,
+ UiAutomatorUtils2.waitFindObject(By.res(ALLOW_FOREGROUND_ONLY_RADIO_BUTTON))
+ .isChecked
+ )
+ assertEquals(
+ askChecked,
+ UiAutomatorUtils2.waitFindObject(By.res(ASK_RADIO_BUTTON)).isChecked
+ )
+ assertEquals(
+ denyChecked,
+ UiAutomatorUtils2.waitFindObject(By.res(DENY_RADIO_BUTTON)).isChecked
+ )
+ }
+ }
+
+ private fun openAppPermissionsScreen() {
+ UiAutomatorUtils2.getUiDevice()
+ .performActionAndWait(
+ {
+ defaultDeviceContext.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ )
+ },
+ Until.newWindow(),
+ NEW_WINDOW_TIMEOUT_MILLIS
+ )
+ }
+
+ private fun getScrollableRecyclerView(): UiScrollable {
+ // Wait for object to load
+ UiAutomatorUtils2.waitFindObject(By.res(RECYCLER_VIEW))
+ return UiScrollable(UiSelector().resourceId(RECYCLER_VIEW))
+ }
+
+ private fun clickPermissionItem(permissionItemName: String) =
+ UiAutomatorUtils2.waitFindObject(By.text(permissionItemName)).click()
+
+ private fun clickAllowForegroundButton() =
+ UiAutomatorUtils2.waitFindObject(By.res(ALLOW_FOREGROUND_ONLY_RADIO_BUTTON)).click()
+
+ private fun clickAskButton() =
+ UiAutomatorUtils2.waitFindObject(By.res(ASK_RADIO_BUTTON)).click()
+
+ private fun clickDenyButton() =
+ UiAutomatorUtils2.waitFindObject(By.res(DENY_RADIO_BUTTON)).click()
+
+ private fun grantRunTimePermission() =
+ permissionManager.grantRuntimePermission(
+ APP_PACKAGE_NAME,
+ DEVICE_AWARE_PERMISSION,
+ persistentDeviceId
+ )
+
+ private fun getPermState(): Map<String, PermissionManager.PermissionState> =
+ permissionManager.getAllPermissionStates(APP_PACKAGE_NAME, persistentDeviceId)
+
+ companion object {
+ private const val APK_DIRECTORY = "/data/local/tmp/cts-permissionmultidevice"
+ private const val APP_APK_PATH_STREAMING =
+ "${APK_DIRECTORY}/CtsAccessRemoteDeviceCamera.apk"
+ private const val APP_PACKAGE_NAME =
+ "android.permissionmultidevice.cts.accessremotedevicecamera"
+ private const val DEVICE_AWARE_PERMISSION = Manifest.permission.CAMERA
+
+ private const val ALLOW_FOREGROUND_ONLY_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/allow_foreground_only_radio_button"
+ private const val ASK_RADIO_BUTTON = "com.android.permissioncontroller:id/ask_radio_button"
+ private const val DENY_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/deny_radio_button"
+ private const val TITLE = "android:id/title"
+ private const val RECYCLER_VIEW = "com.android.permissioncontroller:id/recycler_view"
+ private const val PERMISSION_MESSAGE_ID =
+ "com.android.permissioncontroller:id/permission_message"
+ private const val NEW_WINDOW_TIMEOUT_MILLIS: Long = 20_000
+ }
+}
diff --git a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
new file mode 100644
index 000000000..c7f23377a
--- /dev/null
+++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt
@@ -0,0 +1,278 @@
+/*
+ * 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 android.permissionmultidevice.cts
+
+import android.Manifest
+import android.app.Instrumentation
+import android.companion.virtual.VirtualDeviceManager
+import android.companion.virtual.VirtualDeviceParams
+import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM
+import android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA
+import android.content.ComponentName
+import android.content.Intent
+import android.content.Intent.EXTRA_RESULT_RECEIVER
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.os.Build
+import android.os.Bundle
+import android.os.RemoteCallback
+import android.permission.flags.Flags
+import android.permissionmultidevice.cts.PackageManagementUtils.installPackage
+import android.permissionmultidevice.cts.PackageManagementUtils.uninstallPackage
+import android.permissionmultidevice.cts.UiAutomatorUtils.click
+import android.permissionmultidevice.cts.UiAutomatorUtils.findTextForView
+import android.permissionmultidevice.cts.UiAutomatorUtils.waitFindObject
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.view.Display
+import android.virtualdevice.cts.common.VirtualDeviceRule
+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.SystemUtil
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+@AppModeFull(reason = "VirtualDeviceManager cannot be accessed by instant apps")
+class DeviceAwarePermissionGrantTest {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val defaultDeviceContext = instrumentation.targetContext
+ private lateinit var virtualDeviceManager: VirtualDeviceManager
+ private lateinit var virtualDevice: VirtualDeviceManager.VirtualDevice
+ private lateinit var virtualDisplay: VirtualDisplay
+ private lateinit var deviceDisplayName: String
+
+ @get:Rule var virtualDeviceRule = VirtualDeviceRule.createDefault()
+
+ @Before
+ fun setup() {
+ assumeFalse(PermissionUtils.isAutomotive(defaultDeviceContext))
+ assumeFalse(PermissionUtils.isTv(defaultDeviceContext))
+ assumeFalse(PermissionUtils.isWatch(defaultDeviceContext))
+
+ installPackage(APP_APK_PATH_STREAMING)
+ virtualDeviceManager =
+ defaultDeviceContext.getSystemService(VirtualDeviceManager::class.java)!!
+ virtualDevice =
+ virtualDeviceRule.createManagedVirtualDevice(
+ VirtualDeviceParams.Builder()
+ .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_CUSTOM)
+ .build()
+ )
+
+ val displayConfig =
+ VirtualDeviceRule.createDefaultVirtualDisplayConfigBuilder(
+ DISPLAY_WIDTH,
+ DISPLAY_HEIGHT
+ )
+ .setFlags(
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC or
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED or
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+ )
+ .build()
+
+ virtualDisplay =
+ virtualDeviceRule.createManagedVirtualDisplay(virtualDevice, displayConfig)!!
+ deviceDisplayName =
+ virtualDeviceManager.getVirtualDevice(virtualDevice.deviceId)!!.displayName.toString()
+ }
+
+ @After
+ fun cleanup() {
+ uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false)
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun onHostDevice_requestPermissionForHostDevice_shouldGrantPermission() {
+ testGrantPermissionForDevice(
+ Display.DEFAULT_DISPLAY,
+ DEVICE_ID_DEFAULT,
+ false,
+ "",
+ expectPermissionGrantedOnDefaultDevice = true,
+ expectPermissionGrantedOnRemoteDevice = false
+ )
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun onHostDevice_requestPermissionForRemoteDevice_shouldGrantPermission() {
+ testGrantPermissionForDevice(
+ Display.DEFAULT_DISPLAY,
+ virtualDevice.deviceId,
+ true,
+ deviceDisplayName,
+ expectPermissionGrantedOnDefaultDevice = false,
+ expectPermissionGrantedOnRemoteDevice = true
+ )
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun onRemoteDevice_requestPermissionForHostDevice_shouldShowWarningDialog() {
+ requestPermissionOnDevice(virtualDisplay.display.displayId, DEVICE_ID_DEFAULT)
+
+ val displayId = virtualDisplay.display.displayId
+ waitFindObject(By.displayId(displayId).textContains("Permission request suppressed"))
+ }
+
+ @RequiresFlagsEnabled(
+ Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED,
+ Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED
+ )
+ @Test
+ fun onRemoteDevice_requestPermissionForRemoteDevice_shouldGrantPermission() {
+ testGrantPermissionForDevice(
+ virtualDisplay.display.displayId,
+ virtualDevice.deviceId,
+ true,
+ deviceDisplayName,
+ expectPermissionGrantedOnDefaultDevice = false,
+ expectPermissionGrantedOnRemoteDevice = true
+ )
+ }
+
+ private fun testGrantPermissionForDevice(
+ displayId: Int,
+ targetDeviceId: Int,
+ showDeviceName: Boolean,
+ expectedDeviceNameInDialog: String,
+ expectPermissionGrantedOnDefaultDevice: Boolean,
+ expectPermissionGrantedOnRemoteDevice: Boolean
+ ) {
+ // Assert no permission granted to either default device or virtual device at the beginning
+ assertAppHasPermissionForDevice(DEVICE_ID_DEFAULT, false)
+ assertAppHasPermissionForDevice(virtualDevice.deviceId, false)
+
+ val future = requestPermissionOnDevice(displayId, targetDeviceId)
+ virtualDeviceRule.waitAndAssertActivityResumed(getPermissionDialogComponentName())
+
+ if (showDeviceName) {
+ assertPermissionMessageContainsDeviceName(displayId, expectedDeviceNameInDialog)
+ }
+
+ // Click the allow button in the dialog to grant permission
+ SystemUtil.eventually { click(By.displayId(displayId).res(ALLOW_BUTTON)) }
+
+ // Validate permission grant result returned from callback
+ val grantPermissionResult = future.get(TIMEOUT, TimeUnit.MILLISECONDS)
+ assertThat(
+ grantPermissionResult.getStringArray(
+ TestConstants.PERMISSION_RESULT_KEY_PERMISSIONS
+ )
+ )
+ .isEqualTo(arrayOf(DEVICE_AWARE_PERMISSION))
+ assertThat(
+ grantPermissionResult.getIntArray(TestConstants.PERMISSION_RESULT_KEY_GRANT_RESULTS)
+ )
+ .isEqualTo(arrayOf(PackageManager.PERMISSION_GRANTED).toIntArray())
+ assertThat(grantPermissionResult.getInt(TestConstants.PERMISSION_RESULT_KEY_DEVICE_ID))
+ .isEqualTo(targetDeviceId)
+
+ // Validate whether permission is granted as expected
+ assertAppHasPermissionForDevice(DEVICE_ID_DEFAULT, expectPermissionGrantedOnDefaultDevice)
+ assertAppHasPermissionForDevice(
+ virtualDevice.deviceId,
+ expectPermissionGrantedOnRemoteDevice
+ )
+ }
+
+ private fun requestPermissionOnDevice(
+ displayId: Int,
+ targetDeviceId: Int
+ ): CompletableFuture<Bundle> {
+ val future = CompletableFuture<Bundle>()
+ val callback = RemoteCallback { result: Bundle? -> future.complete(result) }
+ val intent =
+ Intent()
+ .setComponent(
+ ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.RequestPermissionActivity")
+ )
+ .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, targetDeviceId)
+ .putExtra(EXTRA_RESULT_RECEIVER, callback)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ virtualDeviceRule.sendIntentToDisplay(intent, displayId)
+
+ return future
+ }
+
+ private fun assertPermissionMessageContainsDeviceName(displayId: Int, deviceName: String) {
+ waitFindObject(By.displayId(displayId).res(PERMISSION_MESSAGE_ID))
+ val text = findTextForView(By.displayId(displayId).res(PERMISSION_MESSAGE_ID))
+ Truth.assertThat(text).contains(deviceName)
+ }
+
+ private fun assertAppHasPermissionForDevice(deviceId: Int, expectPermissionGranted: Boolean) {
+ val checkPermissionResult =
+ defaultDeviceContext
+ .createDeviceContext(deviceId)
+ .packageManager
+ .checkPermission(DEVICE_AWARE_PERMISSION, APP_PACKAGE_NAME)
+
+ if (expectPermissionGranted) {
+ Assert.assertEquals(PackageManager.PERMISSION_GRANTED, checkPermissionResult)
+ } else {
+ Assert.assertEquals(PackageManager.PERMISSION_DENIED, checkPermissionResult)
+ }
+ }
+
+ private fun getPermissionDialogComponentName(): ComponentName {
+ val intent = Intent(ACTION_REQUEST_PERMISSIONS)
+ intent.setPackage(defaultDeviceContext.packageManager.getPermissionControllerPackageName())
+ return intent.resolveActivity(defaultDeviceContext.packageManager)
+ }
+
+ companion object {
+ const val APK_DIRECTORY = "/data/local/tmp/cts-permissionmultidevice"
+ const val APP_APK_PATH_STREAMING = "${APK_DIRECTORY}/CtsAccessRemoteDeviceCamera.apk"
+ const val APP_PACKAGE_NAME = "android.permissionmultidevice.cts.accessremotedevicecamera"
+ const val PERMISSION_MESSAGE_ID = "com.android.permissioncontroller:id/permission_message"
+ const val ALLOW_BUTTON =
+ "com.android.permissioncontroller:id/permission_allow_foreground_only_button"
+ const val DEVICE_ID_DEFAULT = 0
+ const val PERSISTENT_DEVICE_ID_DEFAULT = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ const val DEVICE_AWARE_PERMISSION = Manifest.permission.CAMERA
+ const val TIMEOUT = 5000L
+ private const val DISPLAY_HEIGHT = 1920
+ private const val DISPLAY_WIDTH = 1080
+ }
+}
diff --git a/tests/cts/permissionmultiuser/Android.bp b/tests/cts/permissionmultiuser/Android.bp
new file mode 100644
index 000000000..b86b02205
--- /dev/null
+++ b/tests/cts/permissionmultiuser/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsPermissionMultiUserTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ min_sdk_version: "30",
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "Harrier",
+ "modules-utils-build_system",
+ "Nene",
+ ],
+ data: [
+ ":CtsRequestLocationApp",
+ ],
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "mcts-permission",
+ ],
+}
diff --git a/tests/cts/permissionmultiuser/AndroidManifest.xml b/tests/cts/permissionmultiuser/AndroidManifest.xml
new file mode 100644
index 000000000..15bc3af34
--- /dev/null
+++ b/tests/cts/permissionmultiuser/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionmultiuser.cts">
+
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permissionmultiuser.cts"
+ android:label="CTS multi-user tests for permissions">
+ </instrumentation>
+</manifest>
diff --git a/tests/cts/permissionmultiuser/AndroidTest.xml b/tests/cts/permissionmultiuser/AndroidTest.xml
new file mode 100644
index 000000000..10fd4e7a5
--- /dev/null
+++ b/tests/cts/permissionmultiuser/AndroidTest.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<configuration description="Config for CTS permissionmultiuser test cases">
+
+ <option name="test-suite-tag" value="cts" />
+
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="multiuser" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ <option name="disable-device-config-sync" value="true" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionMultiUserTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="CtsRequestLocationApp.apk->/data/local/tmp/cts-permissionmultiuser/CtsRequestLocationApp.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="appops set android.permissionmultiuser.cts REQUEST_INSTALL_PACKAGES allow" />
+ <!-- disable DeprecatedAbi warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <!-- disable DeprecatedTargetSdk warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
+ </target_preparer>
+
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-permissionmultiuser" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionmultiuser"/>
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permissionmultiuser.cts" />
+ <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" />
+ <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+
+</configuration>
diff --git a/tests/cts/permissionmultiuser/OWNERS b/tests/cts/permissionmultiuser/OWNERS
new file mode 100644
index 000000000..fb6099cf7
--- /dev/null
+++ b/tests/cts/permissionmultiuser/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/cts/permissionmultiuser/RequestLocationApp/Android.bp b/tests/cts/permissionmultiuser/RequestLocationApp/Android.bp
new file mode 100644
index 000000000..d645e15d7
--- /dev/null
+++ b/tests/cts/permissionmultiuser/RequestLocationApp/Android.bp
@@ -0,0 +1,25 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsRequestLocationApp",
+ target_sdk_version: "30",
+ min_sdk_version: "30",
+}
diff --git a/tests/cts/permissionmultiuser/RequestLocationApp/AndroidManifest.xml b/tests/cts/permissionmultiuser/RequestLocationApp/AndroidManifest.xml
new file mode 100644
index 000000000..e5e7609eb
--- /dev/null
+++ b/tests/cts/permissionmultiuser/RequestLocationApp/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionmultiuser.cts.requestlocation">
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <application>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionmultiuser/TEST_MAPPING b/tests/cts/permissionmultiuser/TEST_MAPPING
new file mode 100644
index 000000000..48f5ef537
--- /dev/null
+++ b/tests/cts/permissionmultiuser/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPermissionMultiUserTestCases"
+ }
+ ]
+}
diff --git a/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt b/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt
new file mode 100644
index 000000000..2169f0f72
--- /dev/null
+++ b/tests/cts/permissionmultiuser/src/android/permissionmultiuser/cts/AppDataSharingUpdatesTest.kt
@@ -0,0 +1,493 @@
+/*
+ * 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 android.permissionmultiuser.cts
+
+import android.app.Instrumentation
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_MUTABLE
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.app.UiAutomation
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Context.RECEIVER_EXPORTED
+import android.content.Intent
+import android.content.Intent.ACTION_REVIEW_APP_DATA_SHARING_UPDATES
+import android.content.IntentFilter
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageInstaller.EXTRA_STATUS
+import android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE
+import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID
+import android.content.pm.PackageInstaller.STATUS_SUCCESS
+import android.content.pm.PackageInstaller.SessionParams
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FEATURE_AUTOMOTIVE
+import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.os.Build
+import android.os.PersistableBundle
+import android.os.SystemClock
+import android.os.UserHandle
+import android.provider.DeviceConfig
+import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.BySelector
+import android.support.test.uiautomator.StaleObjectException
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiObject2
+import android.util.Log
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.permissions.annotations.EnsureHasPermission
+import com.android.bedstead.harrier.annotations.EnsureSecureSettingSet
+import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature
+import com.android.bedstead.harrier.annotations.RequireNotWatch
+import com.android.bedstead.harrier.annotations.RequireRunOnAdditionalUser
+import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile
+import com.android.bedstead.harrier.annotations.RequireSdkVersion
+import com.android.bedstead.permissions.CommonPermissions.INTERACT_ACROSS_USERS
+import com.android.compatibility.common.util.ApiTest
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.waitForBroadcasts
+import com.android.compatibility.common.util.UiAutomatorUtils
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests the UI that displays information about apps' updates to their data sharing policies when
+ * device has multiple users.
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@RequireSdkVersion(min = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+@RequireDoesNotHaveFeature(FEATURE_AUTOMOTIVE)
+@RequireDoesNotHaveFeature(FEATURE_LEANBACK)
+@RequireNotWatch(reason = "Data sharing update page is unavailable on watch")
+@RunWith(BedsteadJUnit4::class)
+@EnsureSecureSettingSet(key = "user_setup_complete", value = "1")
+class AppDataSharingUpdatesTest {
+
+ @get:Rule
+ val deviceConfigSafetyLabelChangeNotificationsEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED,
+ true.toString()
+ )
+
+ @get:Rule
+ val deviceConfigDataSharingUpdatesPeriod =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS,
+ "600000"
+ )
+
+ /**
+ * This rule serves to limit the max number of safety labels that can be persisted, so that
+ * repeated tests don't overwhelm the disk storage on the device.
+ */
+ @get:Rule
+ val deviceConfigMaxSafetyLabelsPersistedPerApp =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP,
+ "2"
+ )
+
+ @Before
+ fun registerInstallSessionResultReceiver() {
+ context.registerReceiver(
+ installSessionResultReceiver,
+ IntentFilter(INSTALL_ACTION_CALLBACK),
+ RECEIVER_EXPORTED
+ )
+ }
+
+ @After
+ fun unregisterInstallSessionResultReceiver() {
+ try {
+ context.unregisterReceiver(installSessionResultReceiver)
+ } catch (ignored: IllegalArgumentException) {}
+ }
+
+ @Test
+ @EnsureHasPermission(INTERACT_ACROSS_USERS)
+ @RequireRunOnWorkProfile
+ @ApiTest(apis = ["android.content.Intent#ACTION_REVIEW_APP_DATA_SHARING_UPDATES"])
+ fun openDataSharingUpdatesPage_workProfile_whenAppHasUpdateAndLocationGranted_showUpdates() {
+ installPackageViaSession(LOCATION_PACKAGE_APK_PATH, createAppMetadataWithNoSharing())
+ waitForBroadcasts()
+ installPackageViaSession(
+ LOCATION_PACKAGE_APK_PATH,
+ createAppMetadataWithLocationSharingNoAds()
+ )
+ waitForBroadcasts()
+ grantLocationPermission(LOCATION_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivityForUser(deviceState.initialUser().userHandle())
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(LOCATION_PACKAGE_NAME_SUBSTRING), true)
+ } finally {
+ pressBack()
+ uninstallPackage(LOCATION_PACKAGE_NAME)
+ }
+ }
+
+ @Test
+ @EnsureHasPermission(INTERACT_ACROSS_USERS)
+ @RequireRunOnAdditionalUser
+ @ApiTest(apis = ["android.content.Intent#ACTION_REVIEW_APP_DATA_SHARING_UPDATES"])
+ fun openDataSharingUpdatesPage_additionalUser_whenAppHasUpdateAndLocationGranted_showUpdates() {
+ installPackageViaSession(LOCATION_PACKAGE_APK_PATH, createAppMetadataWithNoSharing())
+ waitForBroadcasts()
+ installPackageViaSession(
+ LOCATION_PACKAGE_APK_PATH,
+ createAppMetadataWithLocationSharingNoAds()
+ )
+ waitForBroadcasts()
+ grantLocationPermission(LOCATION_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivityForUser(deviceState.additionalUser().userHandle())
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(LOCATION_PACKAGE_NAME_SUBSTRING), true)
+ } finally {
+ pressBack()
+ uninstallPackage(LOCATION_PACKAGE_NAME)
+ }
+
+ deviceState.initialUser().switchTo()
+
+ startAppDataSharingUpdatesActivityForUser(deviceState.initialUser().userHandle())
+
+ try {
+ // Verify that state does not leak across users.
+ assertNoUpdatesPresent()
+ findView(By.textContains(LOCATION_PACKAGE_NAME_SUBSTRING), false)
+ } finally {
+ pressBack()
+ }
+ }
+
+ /** Companion object for [AppDataSharingUpdatesTest]. */
+ companion object {
+ @JvmField @ClassRule @Rule val deviceState: DeviceState = DeviceState()
+
+ @JvmStatic
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.context
+ @JvmStatic private val uiAutomation: UiAutomation = instrumentation.uiAutomation
+ @JvmStatic private val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+ @JvmStatic private val packageManager: PackageManager = context.packageManager
+ @JvmStatic private val packageInstaller = packageManager.packageInstaller
+ private data class SessionResult(val status: Int?)
+ private val TAG = AppDataSharingUpdatesTest::class.simpleName
+
+ private const val APK_DIRECTORY = "/data/local/tmp/cts-permissionmultiuser"
+ private const val LOCATION_PACKAGE_NAME = "android.permissionmultiuser.cts.requestlocation"
+ private const val LOCATION_PACKAGE_APK_PATH = "CtsRequestLocationApp.apk"
+ private const val INSTALL_ACTION_CALLBACK = "AppDataSharingUpdatesTest.install_callback"
+ private const val PACKAGE_INSTALLER_TIMEOUT = 60000L
+ private const val IDLE_TIMEOUT_MILLIS: Long = 1000
+ private const val TIMEOUT_MILLIS: Long = 20000
+
+ private const val KEY_VERSION = "version"
+ private const val KEY_SAFETY_LABELS = "safety_labels"
+ private const val KEY_DATA_SHARED = "data_shared"
+ private const val KEY_DATA_LABELS = "data_labels"
+ private const val KEY_PURPOSES = "purposes"
+ private const val INITIAL_SAFETY_LABELS_VERSION = 1L
+ private const val INITIAL_TOP_LEVEL_VERSION = 1L
+ private const val LOCATION_CATEGORY = "location"
+ private const val APPROX_LOCATION = "approx_location"
+ private const val PURPOSE_FRAUD_PREVENTION_SECURITY = 4
+
+ private const val DATA_SHARING_UPDATES = "Data sharing updates for location"
+ private const val DATA_SHARING_UPDATES_SUBTITLE =
+ "These apps have changed the way they may share your location data. They may not" +
+ " have shared it before, or may now share it for advertising or marketing" +
+ " purposes."
+ private const val DATA_SHARING_NO_UPDATES_MESSAGE = "No updates at this time"
+ private const val UPDATES_IN_LAST_30_DAYS = "Updated within 30 days"
+ private const val DATA_SHARING_UPDATES_FOOTER_MESSAGE =
+ "The developers of these apps provided info about their data sharing practices" +
+ " to an app store. They may update it over time.\n\nData sharing" +
+ " practices may vary based on your app version, use, region, and age."
+ private const val LOCATION_PACKAGE_NAME_SUBSTRING = "android.permissionmultiuser"
+ private const val PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS =
+ "data_sharing_update_period_millis"
+ private const val PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP =
+ "max_safety_labels_persisted_per_app"
+
+ private var installSessionResult = LinkedBlockingQueue<SessionResult>()
+
+ private val installSessionResultReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID)
+ val msg = intent.getStringExtra(EXTRA_STATUS_MESSAGE)
+ Log.d(TAG, "status: $status, msg: $msg")
+
+ installSessionResult.offer(SessionResult(status))
+ }
+ }
+
+ /** Installs an app with the provided [appMetadata] */
+ private fun installPackageViaSession(
+ apkName: String,
+ appMetadata: PersistableBundle? = null,
+ packageSource: Int? = null
+ ) {
+ val session = createPackageInstallerSession(packageSource)
+ runWithShellPermissionIdentity {
+ writePackageInstallerSession(session, apkName)
+ if (appMetadata != null) {
+ setAppMetadata(session, appMetadata)
+ }
+ commitPackageInstallerSession(session)
+
+ // No need to click installer UI here due to running in shell permission identity
+ // and not needing user interaction to complete install.
+ // Install should have succeeded.
+ val result = getInstallSessionResult()
+ assertThat(result.status).isEqualTo(STATUS_SUCCESS)
+ }
+ }
+
+ private fun createPackageInstallerSession(
+ packageSource: Int? = null
+ ): PackageInstaller.Session {
+ val sessionParam = SessionParams(SessionParams.MODE_FULL_INSTALL)
+ if (packageSource != null) {
+ sessionParam.setPackageSource(packageSource)
+ }
+
+ val sessionId = packageInstaller.createSession(sessionParam)
+ return packageInstaller.openSession(sessionId)
+ }
+
+ private fun writePackageInstallerSession(
+ session: PackageInstaller.Session,
+ apkName: String
+ ) {
+ val apkFile = File(APK_DIRECTORY, apkName)
+ apkFile.inputStream().use { fileOnDisk ->
+ session
+ .openWrite(/* name= */ apkName, /* offsetBytes= */ 0, /* lengthBytes= */ -1)
+ .use { sessionFile -> fileOnDisk.copyTo(sessionFile) }
+ }
+ }
+
+ private fun commitPackageInstallerSession(session: PackageInstaller.Session) {
+ // PendingIntent that triggers a INSTALL_ACTION_CALLBACK broadcast that gets received by
+ // installSessionResultReceiver when install actions occur with this session
+ val installActionPendingIntent =
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ Intent(INSTALL_ACTION_CALLBACK).setPackage(context.packageName),
+ FLAG_UPDATE_CURRENT or FLAG_MUTABLE
+ )
+ session.commit(installActionPendingIntent.intentSender)
+ }
+
+ private fun setAppMetadata(session: PackageInstaller.Session, data: PersistableBundle) {
+ try {
+ session.setAppMetadata(data)
+ } catch (e: Exception) {
+ session.abandon()
+ throw e
+ }
+ }
+
+ private fun getInstallSessionResult(
+ timeout: Long = PACKAGE_INSTALLER_TIMEOUT
+ ): SessionResult {
+ return installSessionResult.poll(timeout, TimeUnit.MILLISECONDS)
+ ?: SessionResult(null /* status */)
+ }
+
+ private fun uninstallPackage(packageName: String) {
+ runShellCommand("pm uninstall $packageName").trim()
+ }
+
+ private fun pressBack() {
+ uiDevice.pressBack()
+ uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+ }
+
+ /** Returns an App Metadata [PersistableBundle] representation where no data is shared. */
+ private fun createAppMetadataWithNoSharing(): PersistableBundle {
+ return createMetadataWithDataShared(PersistableBundle())
+ }
+
+ /**
+ * Returns an App Metadata [PersistableBundle] representation where location data is shared,
+ * but not for advertising purpose.
+ */
+ private fun createAppMetadataWithLocationSharingNoAds(): PersistableBundle {
+ val locationBundle =
+ PersistableBundle().apply {
+ putPersistableBundle(
+ APPROX_LOCATION,
+ PersistableBundle().apply {
+ putIntArray(
+ KEY_PURPOSES,
+ listOf(PURPOSE_FRAUD_PREVENTION_SECURITY).toIntArray()
+ )
+ }
+ )
+ }
+
+ val dataSharedBundle =
+ PersistableBundle().apply {
+ putPersistableBundle(LOCATION_CATEGORY, locationBundle)
+ }
+
+ return createMetadataWithDataShared(dataSharedBundle)
+ }
+
+ /**
+ * Returns an App Metadata [PersistableBundle] representation where with the provided data
+ * shared.
+ */
+ private fun createMetadataWithDataShared(
+ dataSharedBundle: PersistableBundle
+ ): PersistableBundle {
+ val dataLabelBundle =
+ PersistableBundle().apply {
+ putPersistableBundle(KEY_DATA_SHARED, dataSharedBundle)
+ }
+
+ val safetyLabelBundle =
+ PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_SAFETY_LABELS_VERSION)
+ putPersistableBundle(KEY_DATA_LABELS, dataLabelBundle)
+ }
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_SAFETY_LABELS, safetyLabelBundle)
+ }
+ }
+
+ /**
+ * Starts activity with intent [ACTION_REVIEW_APP_DATA_SHARING_UPDATES] for the provided
+ * user.
+ */
+ fun startAppDataSharingUpdatesActivityForUser(userHandle: UserHandle) {
+ runWithShellPermissionIdentity {
+ context.startActivityAsUser(
+ Intent(ACTION_REVIEW_APP_DATA_SHARING_UPDATES).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ },
+ userHandle
+ )
+ }
+ }
+
+ private fun assertUpdatesPresent() {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_SUBTITLE), true)
+ findView(By.textContains(UPDATES_IN_LAST_30_DAYS), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_FOOTER_MESSAGE), true)
+ }
+
+ private fun assertNoUpdatesPresent() {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(DATA_SHARING_NO_UPDATES_MESSAGE), true)
+ findView(By.textContains(LOCATION_PACKAGE_NAME_SUBSTRING), false)
+ }
+
+ private fun grantLocationPermission(packageName: String) {
+ uiAutomation.grantRuntimePermission(
+ packageName,
+ android.Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+
+ protected fun waitFindObject(
+ selector: BySelector,
+ timeoutMillis: Long = 20_000L
+ ): UiObject2 {
+ uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+ val startTime = SystemClock.elapsedRealtime()
+ return try {
+ UiAutomatorUtils.waitFindObject(selector, timeoutMillis)
+ } catch (e: StaleObjectException) {
+ val remainingTime = timeoutMillis - (SystemClock.elapsedRealtime() - startTime)
+ if (remainingTime <= 0) {
+ throw e
+ }
+ UiAutomatorUtils.waitFindObject(selector, remainingTime)
+ }
+ }
+
+ private fun findView(selector: BySelector, expected: Boolean) {
+ val timeoutMillis =
+ if (expected) {
+ 20000L
+ } else {
+ 1000L
+ }
+
+ val exception =
+ try {
+ uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+ val startTime = SystemClock.elapsedRealtime()
+ try {
+ UiAutomatorUtils.waitFindObject(selector, timeoutMillis)
+ } catch (e: StaleObjectException) {
+ val remainingTime =
+ timeoutMillis - (SystemClock.elapsedRealtime() - startTime)
+ if (remainingTime <= 0) {
+ throw e
+ }
+ UiAutomatorUtils.waitFindObject(selector, remainingTime)
+ }
+ null
+ } catch (e: Exception) {
+ e
+ }
+ val actual = exception == null
+ val message =
+ if (expected) {
+ "Expected view $selector not found"
+ } else {
+ "Unexpected view found: $selector"
+ }
+ Assert.assertTrue(message, actual == expected)
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/Android.bp b/tests/cts/permissionpolicy/Android.bp
new file mode 100644
index 000000000..8f3c42b0e
--- /dev/null
+++ b/tests/cts/permissionpolicy/Android.bp
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2009 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: "CtsPermissionPolicyTestCases",
+ defaults: ["cts_defaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ libs: ["android.test.base"],
+ static_libs: [
+ "androidx.test.core",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "guava",
+ "androidx.test.ext.junit-nodeps",
+ "truth",
+ "permission-test-util-lib",
+ "androidx.test.rules",
+ "flag-junit",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ sdk_version: "test_current",
+ data: [
+ ":CtsLocationPermissionsUserSdk22",
+ ":CtsLocationPermissionsUserSdk29",
+ ":CtsSMSCallLogPermissionsUserSdk22",
+ ":CtsSMSCallLogPermissionsUserSdk29",
+ ":CtsStoragePermissionsUserDefaultSdk22",
+ ":CtsStoragePermissionsUserDefaultSdk28",
+ ":CtsStoragePermissionsUserDefaultSdk29",
+ ":CtsStoragePermissionsUserOptInSdk22",
+ ":CtsStoragePermissionsUserOptInSdk28",
+ ":CtsStoragePermissionsUserOptOutSdk29",
+ ":CtsStoragePermissionsPreservedUserOptOutSdk30",
+ ":CtsLegacyStorageNotIsolatedWithSharedUid",
+ ":CtsLegacyStorageIsolatedWithSharedUid",
+ ":CtsLegacyStorageRestrictedWithSharedUid",
+ ":CtsLegacyStorageRestrictedSdk28WithSharedUid",
+ ":CtsStoragePermissionsUserOptOutSdk30",
+ ":CtsSMSRestrictedWithSharedUid",
+ ":CtsSMSNotRestrictedWithSharedUid",
+ ":CtsProcessOutgoingCalls",
+ ],
+}
diff --git a/tests/cts/permissionpolicy/AndroidManifest.xml b/tests/cts/permissionpolicy/AndroidManifest.xml
new file mode 100755
index 000000000..53ddc59e5
--- /dev/null
+++ b/tests/cts/permissionpolicy/AndroidManifest.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2009 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.permissionpolicy.cts" android:targetSandboxVersion="2">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!--
+ This app contains tests to verify specialized permissions, that require the app to have
+ some permissions.
+ -->
+
+ <!-- need ability to send sms, to test that SMS's cannot be received -->
+ <uses-permission android:name="android.permission.SEND_SMS"/>
+
+ <!-- needs read phone numbers to get current phone number for R+ -->
+ <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
+
+ <!-- needs read phone status to get current phone subscription info for R+ -->
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+
+ <!-- need app that has WRITE_SETTINGS but not WRITE_SECURE_SETTINGS -->
+ <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+
+ <!-- need app that has CALL_PHONE but not PROCESS_OUTGOING_CALL -->
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+
+ <!-- need app that has RECORD_AUDIO but not CAPTURE_AUDIO_OUTPUT -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+ <!-- need app that has READ_CONTACTS but not READ_PROFILE -->
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+
+ <!-- need app that has WRITE_CONTACTS but not WRITE_PROFILE -->
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+ <!-- need a permission that would ordinarily be granted, but has a maxSdkVersion that
+ causes it to be withheld under the current API level -->
+ <uses-permission
+ android:name="android.permission.INTERNET"
+ android:maxSdkVersion="18"/>
+
+
+ <!-- need a permission that will be granted -->
+ <uses-permission
+ android:name="android.permission.ACCESS_NETWORK_STATE"
+ android:maxSdkVersion="9000"/>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permissionpolicy.cts"
+ android:label="More CTS tests for permissions">
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/cts/permissionpolicy/AndroidTest.xml b/tests/cts/permissionpolicy/AndroidTest.xml
new file mode 100644
index 000000000..8a5609ab9
--- /dev/null
+++ b/tests/cts/permissionpolicy/AndroidTest.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Permission Policy test cases">
+
+ <option name="test-suite-tag" value="cts" />
+ <option name="not-shardable" value="true" />
+
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <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="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
+
+ <!-- TODO(b/245579250): update to Sdk34 once sdk finalized -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk33ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionPolicyTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ <option name="disable-device-config-sync" value="true" />
+ </target_preparer>
+
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-permissionpolicy" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionpolicy-permissionpolicy"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="CtsLocationPermissionsUserSdk22.apk->/data/local/tmp/cts-permissionpolicy/CtsLocationPermissionsUserSdk22.apk" />
+ <option name="push" value="CtsLocationPermissionsUserSdk29.apk->/data/local/tmp/cts-permissionpolicy/CtsLocationPermissionsUserSdk29.apk" />
+ <option name="push" value="CtsSMSCallLogPermissionsUserSdk22.apk->/data/local/tmp/cts-permissionpolicy/CtsSMSCallLogPermissionsUserSdk22.apk" />
+ <option name="push" value="CtsSMSCallLogPermissionsUserSdk29.apk->/data/local/tmp/cts-permissionpolicy/CtsSMSCallLogPermissionsUserSdk29.apk" />
+ <option name="push" value="CtsStoragePermissionsUserDefaultSdk22.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk22.apk" />
+ <option name="push" value="CtsStoragePermissionsUserDefaultSdk28.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk28.apk" />
+ <option name="push" value="CtsStoragePermissionsUserDefaultSdk29.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk29.apk" />
+ <option name="push" value="CtsStoragePermissionsUserOptInSdk22.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptInSdk22.apk" />
+ <option name="push" value="CtsStoragePermissionsUserOptInSdk28.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptInSdk28.apk" />
+ <option name="push" value="CtsStoragePermissionsUserOptOutSdk29.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptOutSdk29.apk" />
+ <option name="push" value="CtsLegacyStorageNotIsolatedWithSharedUid.apk->/data/local/tmp/cts-permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid.apk" />
+ <option name="push" value="CtsLegacyStorageIsolatedWithSharedUid.apk->/data/local/tmp/cts-permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid.apk" />
+ <option name="push" value="CtsLegacyStorageRestrictedWithSharedUid.apk->/data/local/tmp/cts-permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid.apk" />
+ <option name="push" value="CtsLegacyStorageRestrictedSdk28WithSharedUid.apk->/data/local/tmp/cts-permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid.apk" />
+ <option name="push" value="CtsStoragePermissionsUserOptOutSdk30.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptOutSdk30.apk" />
+ <option name="push" value="CtsStoragePermissionsPreservedUserOptOutSdk30.apk->/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30.apk" />
+ <option name="push" value="CtsSMSRestrictedWithSharedUid.apk->/data/local/tmp/cts-permissionpolicy/CtsSMSRestrictedWithSharedUid.apk" />
+ <option name="push" value="CtsSMSNotRestrictedWithSharedUid.apk->/data/local/tmp/cts-permissionpolicy/CtsSMSNotRestrictedWithSharedUid.apk" />
+ <option name="push" value="CtsProcessOutgoingCalls.apk->/data/local/tmp/cts-permissionpolicy/CtsProcessOutgoingCalls.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- ensure device provisioning and user setup are marked as completed -->
+ <option name="run-command" value="settings put global device_provisioned 1" />
+ <option name="run-command" value="settings put secure user_setup_complete 1" />
+ <!-- disable DeprecatedAbi warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <!-- disable DeprecatedTargetSdk warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permissionpolicy.cts" />
+ <option name="runtime-hint" value="2m" />
+ <!-- This test reads hidden platform flags, allow it accessing the values. -->
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+
+</configuration>
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/Android.bp b/tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
new file mode 100644
index 000000000..146bdceae
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsLegacyStorageIsolatedWithSharedUid",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 000000000..d221b1284
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageIsolatedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.legacystoragewithshareduid.isolated"
+ android:versionCode="1"
+ android:sharedUserId="android.permissionpolicy.cts.restrictedpermissionuser.shareduid">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application android:label="CtsLegacyStorageIsolatedWithSharedUid" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp b/tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
new file mode 100644
index 000000000..c4bc761ce
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsLegacyStorageNotIsolatedWithSharedUid",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 000000000..69188d392
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageNotIsolatedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.legacystoragewithshareduid.notisolated"
+ android:versionCode="1"
+ android:sharedUserId="android.permissionpolicy.cts.restrictedpermissionuser.shareduid">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+
+ <application android:label="CtsLegacyStorageNotIsolatedWithSharedUid"
+ android:requestLegacyExternalStorage="true" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
new file mode 100644
index 000000000..2373fc1e2
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsLegacyStorageRestrictedSdk28WithSharedUid",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml
new file mode 100644
index 000000000..cf09f33a3
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedSdk28WithSharedUid/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.legacystoragewithshareduid.restrictedsdk28"
+ android:versionCode="1"
+ android:sharedUserId="android.permissionpolicy.cts.restrictedpermissionuser.shareduid">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application android:label="CtsLegacyStorageRestrictedSdk28WithSharedUid" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/Android.bp b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
new file mode 100644
index 000000000..523f74224
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsLegacyStorageRestrictedWithSharedUid",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 000000000..3e9acc9aa
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLegacyStorageRestrictedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.legacystoragewithshareduid.restricted"
+ android:versionCode="1"
+ android:sharedUserId="android.permissionpolicy.cts.restrictedpermissionuser.shareduid">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application android:label="CtsLegacyStorageRestrictedWithSharedUid" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/Android.bp b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/Android.bp
new file mode 100644
index 000000000..787cbaebe
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsLocationPermissionsUserSdk22",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // TODO: Uncomment when uncommenting the test
+ // srcs: ["src/**/*.java"]
+
+}
diff --git a/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/AndroidManifest.xml
new file mode 100644
index 000000000..f0732755f
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk22/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22"/>
+
+ <application android:label="CtsLocationPermissionsUserSdk22">
+ </application>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/Android.bp b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/Android.bp
new file mode 100644
index 000000000..93c8b72b3
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsLocationPermissionsUserSdk29",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // TODO: Uncomment when uncommenting the test
+ // srcs: ["src/**/*.java"]
+
+}
diff --git a/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/AndroidManifest.xml
new file mode 100644
index 000000000..21c73cc07
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsLocationPermissionsUserSdk29/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application android:label="CtsLocationPermissionsUserSdk29">
+ </application>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/Android.bp b/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/Android.bp
new file mode 100644
index 000000000..ef6f44b5a
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsProcessOutgoingCalls",
+ defaults: ["cts_defaults"],
+ sdk_version: "test_current",
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+}
diff --git a/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/AndroidManifest.xml
new file mode 100644
index 000000000..254d7d9ce
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionpolicy.cts.receivecallbroadcast">
+
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
+
+ <application>
+ <activity android:name=".ProcessOutgoingCallReceiver$BaseActivity" android:exported="true"/>
+ <receiver android:name=".ProcessOutgoingCallReceiver" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/src/android/permissionpolicy/cts/receivecallbroadcast/ProcessOutgoingCallReceiver.kt b/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/src/android/permissionpolicy/cts/receivecallbroadcast/ProcessOutgoingCallReceiver.kt
new file mode 100644
index 000000000..901810aa1
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsProcessOutgoingCalls/src/android/permissionpolicy/cts/receivecallbroadcast/ProcessOutgoingCallReceiver.kt
@@ -0,0 +1,36 @@
+/*
+ * 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 android.permissionpolicy.cts.receivecallbroadcast
+
+import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+
+class ProcessOutgoingCallReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ context!!.sendBroadcast(
+ Intent(ACTION_TEST_APP_RECEIVED_OUTGOING_CALL).setPackage(TEST_CLASS_PKG_NAME)
+ )
+ }
+
+ class BaseActivity : Activity()
+}
+
+const val TEST_CLASS_PKG_NAME = "android.permissionpolicy.cts"
+const val ACTION_TEST_APP_RECEIVED_OUTGOING_CALL =
+ "android.permissionpolicy.cts.TEST_APP_RECEIVED_CALL"
diff --git a/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/Android.bp b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/Android.bp
new file mode 100644
index 000000000..b864a4e26
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsSMSCallLogPermissionsUserSdk22",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // TODO: Uncomment when uncommenting the test
+ // srcs: ["src/**/*.java"]
+
+}
diff --git a/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/AndroidManifest.xml
new file mode 100644
index 000000000..3d1207360
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk22/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- SMS -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+ <uses-permission android:name="android.permission.RECEIVE_MMS" />
+ <uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
+
+ <!-- CallLog -->
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22"/>
+
+ <application android:label="CtsSMSCallLogPermissionsUserSdk22">
+ </application>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/Android.bp b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/Android.bp
new file mode 100644
index 000000000..d3a2e30f0
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsSMSCallLogPermissionsUserSdk29",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+
+ // TODO: Uncomment when uncommenting the test
+ // srcs: ["src/**/*.java"]
+
+}
diff --git a/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/AndroidManifest.xml
new file mode 100644
index 000000000..035e2e495
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSCallLogPermissionsUserSdk29/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- SMS -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+ <uses-permission android:name="android.permission.RECEIVE_MMS" />
+ <uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
+
+ <!-- CallLog -->
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
+
+ <application android:label="CtsSMSCallLogPermissionsUserSdk29">
+ </application>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/Android.bp b/tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/Android.bp
new file mode 100644
index 000000000..ab90dba35
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsSMSNotRestrictedWithSharedUid",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 000000000..109a9e8df
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSNotRestrictedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.smswithshareduid.notrestricted"
+ android:versionCode="1"
+ android:sharedUserId="android.permissionpolicy.cts.restrictedpermissionuser.shareduid">
+
+ <uses-permission android:name="android.permission.READ_SMS" />
+
+ <application android:label="CtsSMSNotRestrictedWithSharedUid" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/Android.bp b/tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/Android.bp
new file mode 100644
index 000000000..8820ab776
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsSMSRestrictedWithSharedUid",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml
new file mode 100644
index 000000000..cb44afdd1
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsSMSRestrictedWithSharedUid/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.smswithshareduid.restricted"
+ android:versionCode="1"
+ android:sharedUserId="android.permissionpolicy.cts.restrictedpermissionuser.shareduid">
+
+ <uses-permission android:name="android.permission.READ_SMS" />
+
+ <application android:label="CtsSMSRestrictedWithSharedUid" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/Android.bp
new file mode 100644
index 000000000..6c76c7485
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/Android.bp
@@ -0,0 +1,26 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsStoragePermissionsPreservedUserOptOutSdk30",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/AndroidManifest.xml
new file mode 100644
index 000000000..48c4f37c9
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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="android.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:targetSdkVersion="30"/>
+
+ <application android:label="CtsStoragePermissionsPreservedUserOptOutSdk30"
+ android:preserveLegacyExternalStorage="true"/>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/Android.bp
new file mode 100644
index 000000000..bb817f780
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/Android.bp
@@ -0,0 +1,24 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserDefaultSdk22",
+ defaults: ["cts_defaults"],
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/AndroidManifest.xml
new file mode 100644
index 000000000..543b9ebb8
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk22/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22"/>
+
+ <application android:label="CtsStoragePermissionsUserDefaultSdk22" />
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/Android.bp
new file mode 100644
index 000000000..60cb316f6
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/Android.bp
@@ -0,0 +1,24 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserDefaultSdk28",
+ defaults: ["cts_defaults"],
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/AndroidManifest.xml
new file mode 100644
index 000000000..77920de15
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk28/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>
+
+ <application android:label="CtsStoragePermissionsUserDefaultSdk28" />
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/Android.bp
new file mode 100644
index 000000000..d3d016a63
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserDefaultSdk29",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "29",
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/AndroidManifest.xml
new file mode 100644
index 000000000..e96452fd2
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk29/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:targetSdkVersion="29"/>
+
+ <application android:label="CtsStoragePermissionsUserDefaultSdk29" />
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/Android.bp
new file mode 100644
index 000000000..751342e2f
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserOptOutSdk30",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/AndroidManifest.xml
new file mode 100644
index 000000000..b5c71b373
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserDefaultSdk30/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:targetSdkVersion="30"/>
+
+ <application android:label="CtsStoragePermissionsUserOptOutSdk30"
+ android:requestLegacyExternalStorage="true"/>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/Android.bp
new file mode 100644
index 000000000..21e6aceef
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/Android.bp
@@ -0,0 +1,24 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserOptInSdk22",
+ defaults: ["cts_defaults"],
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/AndroidManifest.xml
new file mode 100644
index 000000000..466d60157
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk22/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22"/>
+
+ <application android:label="CtsStoragePermissionsUserOptInSdk22"
+ android:requestLegacyExternalStorage="false"/>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/Android.bp
new file mode 100644
index 000000000..c7f30a8b6
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/Android.bp
@@ -0,0 +1,24 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserOptInSdk28",
+ defaults: ["cts_defaults"],
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/AndroidManifest.xml
new file mode 100644
index 000000000..151bbbfea
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptInSdk28/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>
+
+ <application android:label="CtsStoragePermissionsUserOptInSdk28"
+ android:requestLegacyExternalStorage="false"/>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/Android.bp b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/Android.bp
new file mode 100644
index 000000000..8ad13f798
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/Android.bp
@@ -0,0 +1,26 @@
+//
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsStoragePermissionsUserOptOutSdk29",
+ defaults: ["cts_defaults"],
+
+ sdk_version: "current",
+}
diff --git a/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/AndroidManifest.xml b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/AndroidManifest.xml
new file mode 100644
index 000000000..e41ee7759
--- /dev/null
+++ b/tests/cts/permissionpolicy/CtsStoragePermissionsUserOptOutSdk29/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.permissionpolicy.cts.restrictedpermissionuser"
+ android:versionCode="1">
+
+ <!-- Storage -->
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29"/>
+
+ <application android:label="CtsStoragePermissionsUserOptOutSdk29"
+ android:requestLegacyExternalStorage="true"/>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/OWNERS b/tests/cts/permissionpolicy/OWNERS
new file mode 100644
index 000000000..3f0256275
--- /dev/null
+++ b/tests/cts/permissionpolicy/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
+
+per-file NoLocationPermissionTest.java = tgunn@google.com
+per-file RestrictedStoragePermissionSharedUidTest.java = nandana@google.com
+per-file RestrictedStoragePermissionTest.java = nandana@google.com
+per-file NoReceiveSmsPermissionTest.java = sasindran@google.com
diff --git a/tests/cts/permissionpolicy/res/raw/OWNERS b/tests/cts/permissionpolicy/res/raw/OWNERS
new file mode 100644
index 000000000..6e1a91b88
--- /dev/null
+++ b/tests/cts/permissionpolicy/res/raw/OWNERS
@@ -0,0 +1,8 @@
+hackbod@google.com
+patb@google.com
+yamasani@google.com
+michaelwr@google.com
+narayan@google.com
+roosa@google.com
+per-file automotive_android_manifest.xml = skeys@google.com
+per-file automotive_android_manifest.xml = file:platform/packages/services/Car:/OWNERS
diff --git a/tests/cts/permissionpolicy/res/raw/android_manifest.xml b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
new file mode 100644
index 000000000..b92287bb3
--- /dev/null
+++ b/tests/cts/permissionpolicy/res/raw/android_manifest.xml
@@ -0,0 +1,8842 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/AndroidManifest.xml
+**
+** Copyright 2006, 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" coreApp="true" android:sharedUserId="android.uid.system"
+ android:sharedUserLabel="@string/android_system_label">
+
+ <!-- ================================================ -->
+ <!-- Special broadcasts that only the system can send -->
+ <!-- ================================================ -->
+ <eat-comment />
+
+ <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
+ <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
+ <protected-broadcast android:name="android.intent.action.USER_PRESENT" />
+ <protected-broadcast android:name="android.intent.action.TIME_SET" />
+ <protected-broadcast android:name="android.intent.action.TIME_TICK" />
+ <protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.DATE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.PRE_BOOT_COMPLETED" />
+ <protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" />
+ <protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED_INTERNAL" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_LOADED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
+ <protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
+ <protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_UNSTOPPED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_INTEGRITY_VERIFICATION" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGES_UNSUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGES_SUSPENSION_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY" />
+ <protected-broadcast android:name="android.intent.action.DISTRACTING_PACKAGES_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_PREFERRED_ACTIVITY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.UID_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
+ <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.SPLIT_CONFIGURATION_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.APPLICATION_LOCALE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.BATTERY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.BATTERY_LEVEL_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.BATTERY_LOW" />
+ <protected-broadcast android:name="android.intent.action.BATTERY_OKAY" />
+ <protected-broadcast android:name="android.intent.action.ACTION_POWER_CONNECTED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_SHUTDOWN" />
+ <protected-broadcast android:name="android.intent.action.CHARGING" />
+ <protected-broadcast android:name="android.intent.action.DISCHARGING" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_LOW" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_FULL" />
+ <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_NOT_FULL" />
+ <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
+ <protected-broadcast android:name="android.intent.action.REBOOT" />
+ <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+ <protected-broadcast android:name="android.intent.action.THERMAL_EVENT" />
+ <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
+ <protected-broadcast android:name="android.intent.action.USER_ADDED" />
+ <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_STARTING" />
+ <protected-broadcast android:name="android.intent.action.USER_STARTED" />
+ <protected-broadcast android:name="android.intent.action.USER_STOPPING" />
+ <protected-broadcast android:name="android.intent.action.USER_STOPPED" />
+ <protected-broadcast android:name="android.intent.action.USER_BACKGROUND" />
+ <protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
+ <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
+ <protected-broadcast android:name="android.intent.action.USER_INITIALIZE" />
+ <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+ <protected-broadcast android:name="android.intent.action.DOMAINS_NEED_VERIFICATION" />
+ <protected-broadcast android:name="android.intent.action.OVERLAY_ADDED" />
+ <protected-broadcast android:name="android.intent.action.OVERLAY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" />
+ <protected-broadcast android:name="android.intent.action.UNARCHIVE_PACKAGE" />
+
+ <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
+ <protected-broadcast android:name="android.os.action.DEVICE_IDLE_MODE_CHANGED" />
+ <protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" />
+ <protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
+ <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED" />
+ <protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_PORTS_CHANGED" />
+ <protected-broadcast android:name="android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED" />
+
+ <!-- @deprecated This is rarely used and will be phased out soon. -->
+ <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
+
+ <protected-broadcast android:name="android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL" />
+
+ <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
+ <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
+ <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE_PRIORITIZED" />
+ <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE_PRIORITIZED" />
+ <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
+ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
+ <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
+
+ <protected-broadcast android:name="android.app.action.USER_ADDED" />
+ <protected-broadcast android:name="android.app.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.app.action.USER_STARTED" />
+ <protected-broadcast android:name="android.app.action.USER_STOPPED" />
+ <protected-broadcast android:name="android.app.action.USER_SWITCHED" />
+
+ <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" />
+ <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" />
+ <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" />
+ <protected-broadcast android:name="android.app.action.SHOW_DEVICE_MONITORING_DIALOG" />
+ <protected-broadcast android:name="android.intent.action.PENDING_INCIDENT_REPORTS_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.INCIDENT_REPORT_READY" />
+
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_UPDATE_OPTIONS" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLE_AND_UPDATE" />
+
+ <protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
+
+ <protected-broadcast android:name="android.app.backup.intent.CLEAR" />
+ <protected-broadcast android:name="android.app.backup.intent.INIT" />
+
+ <protected-broadcast android:name="android.bluetooth.intent.DISCOVERABLE_TIMEOUT" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.UUID" />
+ <protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
+ <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
+ <protected-broadcast android:name="android.bluetooth.device.action.CLASS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.ACL_CONNECTED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.NAME_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.BOND_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.NAME_FAILED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
+ <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
+ <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REPLY" />
+ <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
+ <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
+ <protected-broadcast android:name="android.bluetooth.device.action.SDP_RECORD" />
+ <protected-broadcast android:name="android.bluetooth.device.action.BATTERY_LEVEL_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.device.action.REMOTE_ISSUE_OCCURRED" />
+ <protected-broadcast android:name="android.bluetooth.devicepicker.action.LAUNCH" />
+ <protected-broadcast android:name="android.bluetooth.devicepicker.action.DEVICE_SELECTED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
+ <protected-broadcast
+ android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headset.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
+ <protected-broadcast
+ android:name="android.bluetooth.headsetclient.profile.action.NETWORK_SERVICE_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_DEVICE_AVAILABLE" />
+ <protected-broadcast android:name="android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE" />
+ <protected-broadcast
+ android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" />
+ <protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" />
+ <protected-broadcast
+ android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.input.profile.action.IDLE_TIME_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
+ <protected-broadcast
+ android:name="android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED" />
+ <protected-broadcast
+ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
+ <protected-broadcast
+ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
+ <protected-broadcast
+ android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.HAP_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONF_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED" />
+ <protected-broadcast
+ android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
+ <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
+ <protected-broadcast android:name="android.btopp.intent.action.LIST" />
+ <protected-broadcast android:name="android.btopp.intent.action.OPEN_OUTBOUND" />
+ <protected-broadcast android:name="android.btopp.intent.action.HIDE_COMPLETE" />
+ <protected-broadcast android:name="android.btopp.intent.action.CONFIRM" />
+ <protected-broadcast android:name="android.btopp.intent.action.HIDE" />
+ <protected-broadcast android:name="android.btopp.intent.action.RETRY" />
+ <protected-broadcast android:name="android.btopp.intent.action.OPEN" />
+ <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />
+ <protected-broadcast android:name="android.btopp.intent.action.TRANSFER_COMPLETE" />
+ <protected-broadcast android:name="android.btopp.intent.action.ACCEPT" />
+ <protected-broadcast android:name="android.btopp.intent.action.DECLINE" />
+ <protected-broadcast android:name="com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN" />
+ <protected-broadcast android:name="com.android.bluetooth.pbap.authchall" />
+ <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" />
+ <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />
+ <protected-broadcast android:name="com.android.bluetooth.pbap.authcancelled" />
+ <protected-broadcast android:name="com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT" />
+ <protected-broadcast android:name="com.android.bluetooth.sap.action.DISCONNECT_ACTION" />
+
+ <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
+
+ <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_DETACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+ <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
+
+ <protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
+ <protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
+ <protected-broadcast android:name="android.media.action.MICROPHONE_MUTE_CHANGED" />
+ <protected-broadcast android:name="android.media.action.SPEAKERPHONE_STATE_CHANGED" />
+
+ <protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
+ <protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
+ <protected-broadcast android:name="android.media.VIBRATE_SETTING_CHANGED" />
+ <protected-broadcast android:name="android.media.VOLUME_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.MASTER_VOLUME_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.MASTER_MUTE_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.MASTER_MONO_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.MASTER_BALANCE_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.SCO_AUDIO_STATE_CHANGED" />
+ <protected-broadcast android:name="android.media.ACTION_SCO_AUDIO_STATE_UPDATED" />
+ <protected-broadcast android:name="com.android.server.audio.action.CHECK_MUSIC_ACTIVE" />
+
+ <protected-broadcast android:name="android.intent.action.MEDIA_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_CHECKING" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_NOFS" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_MOUNTED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_SHARED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_UNSHARED" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_BAD_REMOVAL" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
+
+ <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
+ <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+ <!-- @deprecated. Only {@link android.net.ConnectivityManager.CONNECTIVITY_ACTION} is sent. -->
+ <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
+ <protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
+ <protected-broadcast android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED" />
+ <protected-broadcast android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED" />
+ <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" />
+
+ <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
+
+ <!-- For OMAPI -->
+ <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED" />
+
+ <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
+ <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
+ <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
+ <protected-broadcast android:name="android.nfc.action.REQUIRE_UNLOCK_FOR_NFC" />
+ <protected-broadcast android:name="com.android.nfc.action.LLCP_UP" />
+ <protected-broadcast android:name="com.android.nfc.action.LLCP_DOWN" />
+ <protected-broadcast android:name="com.android.nfc.cardemulation.action.CLOSE_TAP_DIALOG" />
+ <protected-broadcast android:name="com.android.nfc.handover.action.ALLOW_CONNECT" />
+ <protected-broadcast android:name="com.android.nfc.handover.action.DENY_CONNECT" />
+ <protected-broadcast android:name="com.android.nfc.handover.action.TIMEOUT_CONNECT" />
+ <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
+ <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" />
+ <protected-broadcast android:name="com.android.nfc_extras.action.AID_SELECTED" />
+ <!-- For NFC to BT handover -->
+ <protected-broadcast android:name="android.btopp.intent.action.WHITELIST_DEVICE" />
+ <protected-broadcast android:name="android.btopp.intent.action.STOP_HANDOVER_TRANSFER" />
+ <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_SEND" />
+ <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE" />
+ <protected-broadcast android:name="com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER" />
+
+ <protected-broadcast android:name="android.net.action.CLEAR_DNS_CACHE" />
+ <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
+
+ <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
+
+ <protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
+ <protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
+ <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+
+ <protected-broadcast android:name="com.android.server.stats.action.TRIGGER_COLLECTION" />
+
+ <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
+ <protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
+ <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
+ <protected-broadcast android:name="com.android.server.WifiManager.action.DEVICE_IDLE" />
+ <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" />
+ <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" />
+ <protected-broadcast android:name="com.android.internal.action.EUICC_FACTORY_RESET" />
+ <protected-broadcast
+ android:name="com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS" />
+ <protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
+ <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
+ <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
+ <protected-broadcast android:name="com.android.server.net.action.SNOOZE_RAPID" />
+ <protected-broadcast android:name="com.android.server.wifi.ACTION_SHOW_SET_RANDOMIZATION_DETAILS" />
+ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP" />
+ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP" />
+ <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" />
+ <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.DISMISS_NOTIFICATION" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.OPEN_WIFI_PREFERENCES" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.OPEN_WIFI_SETTINGS" />
+ <protected-broadcast android:name="com.android.server.wifi.wakeup.TURN_OFF_WIFI_WAKE" />
+ <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
+ <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.action.NETWORK_SETTINGS_RESET" />
+ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT" />
+ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_ICON" />
+ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" />
+ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" />
+ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" />
+ <protected-broadcast android:name="android.net.wifi.action.REFRESH_USER_PROVISIONING" />
+ <protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" />
+ <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.p2p.PEERS_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.p2p.CONNECTION_STATE_CHANGE" />
+ <protected-broadcast android:name="android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED" />
+ <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
+ <!-- This broadcast is no longer sent in S but it should stay protected to avoid third party
+ apps broadcasting this and confusing old system apps that may not have been updated. -->
+ <protected-broadcast android:name="android.net.conn.NETWORK_CONDITIONS_MEASURED" />
+ <protected-broadcast
+ android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
+ <protected-broadcast android:name="android.net.scoring.SCORE_NETWORKS" />
+ <protected-broadcast android:name="android.net.scoring.SCORER_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
+ <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" />
+ <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
+ <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
+ <protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_STATUS" />
+
+ <!-- Legacy -->
+ <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
+ <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
+
+ <protected-broadcast android:name="com.android.server.ACTION_TRIGGER_IDLE" />
+
+ <protected-broadcast android:name="android.intent.action.HDMI_PLUGGED" />
+
+ <protected-broadcast android:name="android.intent.action.PHONE_STATE" />
+
+ <protected-broadcast android:name="android.intent.action.SUB_DEFAULT_CHANGED" />
+
+ <protected-broadcast android:name="android.location.PROVIDERS_CHANGED" />
+ <protected-broadcast android:name="android.location.MODE_CHANGED" />
+ <protected-broadcast android:name="android.location.action.GNSS_CAPABILITIES_CHANGED" />
+
+ <protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
+
+ <protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
+ <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED" />
+ <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_CREATED" />
+ <protected-broadcast android:name="android.provider.action.SMS_MMS_DB_LOST" />
+ <protected-broadcast android:name="android.intent.action.CONTENT_CHANGED" />
+ <protected-broadcast android:name="android.provider.Telephony.MMS_DOWNLOADED" />
+
+ <protected-broadcast
+ android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
+
+ <!-- Defined in RestrictionsManager -->
+ <protected-broadcast android:name="android.content.action.PERMISSION_RESPONSE_RECEIVED" />
+ <protected-broadcast android:name="android.content.action.REQUEST_PERMISSION" />
+
+ <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_STARTED" />
+ <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
+ <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_PROGRESS" />
+ <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
+
+ <protected-broadcast android:name="android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED" />
+
+ <protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_DONE" />
+ <protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED" />
+
+ <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
+ <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.app.action.RESET_PROTECTION_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
+
+ <!-- Added in N -->
+ <protected-broadcast android:name="android.intent.action.ANR" />
+ <protected-broadcast android:name="android.intent.action.CALL" />
+ <protected-broadcast android:name="android.intent.action.CALL_PRIVILEGED" />
+ <protected-broadcast android:name="android.intent.action.DROPBOX_ENTRY_ADDED" />
+ <protected-broadcast android:name="android.intent.action.INPUT_METHOD_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.internal_sim_state_changed" />
+ <protected-broadcast android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
+ <protected-broadcast android:name="android.intent.action.PRECISE_CALL_STATE" />
+ <protected-broadcast android:name="android.intent.action.SUBSCRIPTION_PHONE_STATE" />
+ <protected-broadcast android:name="android.intent.action.USER_INFO_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.USER_UNLOCKED" />
+ <protected-broadcast android:name="android.intent.action.WALLPAPER_CHANGED" />
+
+ <protected-broadcast android:name="android.app.action.DEVICE_POLICY_MANAGER_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.action.CHOOSE_PRIVATE_KEY_ALIAS" />
+ <protected-broadcast android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
+ <protected-broadcast android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
+ <protected-broadcast android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ <protected-broadcast android:name="android.app.action.LOCK_TASK_ENTERING" />
+ <protected-broadcast android:name="android.app.action.LOCK_TASK_EXITING" />
+ <protected-broadcast android:name="android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE" />
+ <protected-broadcast android:name="android.app.action.ACTION_PASSWORD_CHANGED" />
+ <protected-broadcast android:name="android.app.action.ACTION_PASSWORD_EXPIRING" />
+ <protected-broadcast android:name="android.app.action.ACTION_PASSWORD_FAILED" />
+ <protected-broadcast android:name="android.app.action.ACTION_PASSWORD_SUCCEEDED" />
+ <protected-broadcast android:name="com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION" />
+ <protected-broadcast android:name="com.android.server.ACTION_PROFILE_OFF_DEADLINE" />
+ <protected-broadcast android:name="com.android.server.ACTION_TURN_PROFILE_ON_NOTIFICATION" />
+
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_ADDED" />
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNLOCKED" />
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_REMOVED" />
+ <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_PROVISIONED" />
+
+ <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_STATE_CHANGED" />
+ <protected-broadcast android:name="com.android.bluetooth.map.USER_CONFIRM_TIMEOUT" />
+ <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
+ <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
+ <protected-broadcast android:name="android.content.jobscheduler.JOB_DELAY_EXPIRED" />
+ <protected-broadcast android:name="android.content.syncmanager.SYNC_ALARM" />
+ <protected-broadcast android:name="android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.STREAM_DEVICES_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.media.STREAM_MUTE_CHANGED_ACTION" />
+ <protected-broadcast android:name="android.net.sip.SIP_SERVICE_UP" />
+ <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
+ <protected-broadcast android:name="android.os.action.CHARGING" />
+ <protected-broadcast android:name="android.os.action.DISCHARGING" />
+ <protected-broadcast android:name="android.search.action.SEARCHABLES_CHANGED" />
+ <protected-broadcast android:name="android.security.STORAGE_CHANGED" />
+ <protected-broadcast android:name="android.security.action.TRUST_STORE_CHANGED" />
+ <protected-broadcast android:name="android.security.action.KEYCHAIN_CHANGED" />
+ <protected-broadcast android:name="android.security.action.KEY_ACCESS_CHANGED" />
+ <protected-broadcast android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
+ <protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
+ <protected-broadcast android:name="android.telecom.action.POST_CALL" />
+ <protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
+ <protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SECRET_CODE" />
+ <protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
+ <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
+
+ <protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
+ <protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
+ <protected-broadcast android:name="com.android.server.action.NETWORK_STATS_UPDATED" />
+ <protected-broadcast android:name="com.android.server.timedetector.NetworkTimeUpdateService.action.POLL" />
+ <protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" />
+ <protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" />
+ <protected-broadcast android:name="com.android.settings.bluetooth.ACTION_DISMISS_PAIRING" />
+ <protected-broadcast android:name="com.android.settings.network.DELETE_SUBSCRIPTION" />
+ <protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" />
+ <protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
+
+ <protected-broadcast android:name="android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED" />
+ <protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
+ <protected-broadcast android:name="com.android.server.notification.TimeToLiveHelper" />
+ <protected-broadcast android:name="NotificationHistoryDatabase.CLEANUP" />
+ <protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
+ <protected-broadcast android:name="EventConditionProvider.EVALUATE" />
+ <protected-broadcast android:name="SnoozeHelper.EVALUATE" />
+ <protected-broadcast android:name="wifi_scan_available" />
+
+ <protected-broadcast android:name="action.cne.started" />
+ <protected-broadcast android:name="android.content.jobscheduler.JOB_DEADLINE_EXPIRED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW" />
+ <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_SUPL" />
+ <protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
+ <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
+ <protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
+ <protected-broadcast android:name="com.android.server.action.UPDATE_TWILIGHT_STATE" />
+ <protected-broadcast android:name="com.android.server.action.RESET_TWILIGHT_AUTO" />
+ <protected-broadcast android:name="com.android.server.device_idle.STEP_IDLE_STATE" />
+ <protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
+ <protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
+ <protected-broadcast android:name="intent.action.ACTION_RF_BAND_INFO" />
+ <protected-broadcast android:name="android.intent.action.MEDIA_RESOURCE_GRANTED" />
+ <protected-broadcast android:name="android.app.action.NETWORK_LOGS_AVAILABLE" />
+ <protected-broadcast android:name="android.app.action.SECURITY_LOGS_AVAILABLE" />
+ <protected-broadcast android:name="android.app.action.COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED" />
+
+ <protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" />
+ <protected-broadcast android:name="android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED" />
+ <protected-broadcast android:name="android.app.action.APP_BLOCK_STATE_CHANGED" />
+
+ <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
+ <protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
+
+ <protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
+
+ <protected-broadcast android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
+ <protected-broadcast android:name="android.accounts.action.ACCOUNT_REMOVED" />
+ <protected-broadcast android:name="android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" />
+
+ <protected-broadcast android:name="com.android.sync.SYNC_CONN_STATUS_CHANGED" />
+
+ <protected-broadcast android:name="android.net.sip.action.SIP_INCOMING_CALL" />
+ <protected-broadcast android:name="com.android.phone.SIP_ADD_PHONE" />
+ <protected-broadcast android:name="android.net.sip.action.SIP_REMOVE_PROFILE" />
+ <protected-broadcast android:name="android.net.sip.action.SIP_SERVICE_UP" />
+ <protected-broadcast android:name="android.net.sip.action.SIP_CALL_OPTION_CHANGED" />
+ <protected-broadcast android:name="android.net.sip.action.START_SIP" />
+
+ <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_ACL_CONNECTED" />
+ <protected-broadcast android:name="android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED" />
+
+ <protected-broadcast android:name="android.bluetooth.input.profile.action.HANDSHAKE" />
+ <protected-broadcast android:name="android.bluetooth.input.profile.action.REPORT" />
+
+ <protected-broadcast android:name="android.intent.action.TWILIGHT_CHANGED" />
+
+ <protected-broadcast android:name="com.android.server.fingerprint.ACTION_LOCKOUT_RESET" />
+ <protected-broadcast android:name="android.net.wifi.PASSPOINT_ICON_RECEIVED" />
+
+ <protected-broadcast android:name="com.android.server.notification.CountdownConditionProvider" />
+ <protected-broadcast android:name="android.server.notification.action.ENABLE_NAS" />
+ <protected-broadcast android:name="android.server.notification.action.DISABLE_NAS" />
+ <protected-broadcast android:name="android.server.notification.action.LEARNMORE_NAS" />
+
+ <protected-broadcast android:name="com.android.internal.location.ALARM_WAKEUP" />
+ <protected-broadcast android:name="com.android.internal.location.ALARM_TIMEOUT" />
+ <protected-broadcast android:name="android.intent.action.GLOBAL_BUTTON" />
+
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_AVAILABLE" />
+ <protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" />
+ <protected-broadcast android:name="com.android.server.pm.DISABLE_QUIET_MODE_AFTER_UNLOCK" />
+
+ <protected-broadcast android:name="android.intent.action.PROFILE_ACCESSIBLE" />
+ <protected-broadcast android:name="android.intent.action.PROFILE_INACCESSIBLE" />
+
+ <protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" />
+
+ <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
+
+ <protected-broadcast android:name="com.android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED"/>
+
+ <!-- Added in O -->
+ <protected-broadcast android:name="android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED" />
+ <protected-broadcast android:name="com.android.server.wm.ACTION_REVOKE_SYSTEM_ALERT_WINDOW_PERMISSION" />
+ <protected-broadcast android:name="android.media.tv.action.PARENTAL_CONTROLS_ENABLED_CHANGED" />
+
+ <protected-broadcast android:name="android.content.pm.action.SESSION_COMMITTED" />
+ <protected-broadcast android:name="android.os.action.USER_RESTRICTIONS_CHANGED" />
+ <protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT" />
+ <protected-broadcast android:name="android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED" />
+ <protected-broadcast android:name="android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED" />
+ <protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
+
+ <!-- Made protected in P (was introduced in JB-MR2) -->
+ <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+ <protected-broadcast android:name="android.telephony.euicc.action.OTA_STATUS_CHANGED" />
+
+ <!-- Added in P -->
+ <protected-broadcast android:name="android.app.action.PROFILE_OWNER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.TRANSFER_OWNERSHIP_COMPLETE" />
+ <protected-broadcast android:name="android.app.action.AFFILIATED_PROFILE_TRANSFER_OWNERSHIP_COMPLETE" />
+ <protected-broadcast android:name="android.app.action.STATSD_STARTED" />
+ <protected-broadcast android:name="com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET" />
+ <protected-broadcast android:name="com.android.server.biometrics.face.ACTION_LOCKOUT_RESET" />
+
+ <!-- For IdleController -->
+ <protected-broadcast android:name="android.intent.action.DOCK_IDLE" />
+ <protected-broadcast android:name="android.intent.action.DOCK_ACTIVE" />
+
+ <!-- Added in Q -->
+ <protected-broadcast android:name="android.content.pm.action.SESSION_UPDATED" />
+ <protected-broadcast android:name="android.settings.action.GRAYSCALE_CHANGED" />
+
+ <!-- For CarIdlenessTracker -->
+ <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_ON" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.GARAGE_MODE_OFF" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.FORCE_IDLE" />
+ <protected-broadcast android:name="com.android.server.jobscheduler.UNFORCE_IDLE" />
+
+ <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
+
+ <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
+ <!-- Added in R -->
+ <protected-broadcast android:name="android.app.action.RESET_PROTECTION_POLICY_CHANGED" />
+
+ <!-- For tether entitlement recheck-->
+ <protected-broadcast
+ android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
+
+ <!-- Made protected in S (was added in R) -->
+ <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
+
+ <!-- Added in S -->
+ <protected-broadcast android:name="android.scheduling.action.REBOOT_READY" />
+ <protected-broadcast android:name="android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED" />
+ <protected-broadcast android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.action.SHOW_NEW_USER_DISCLAIMER" />
+
+ <!-- Moved from packages/services/Telephony in T -->
+ <protected-broadcast android:name="android.telecom.action.CURRENT_TTY_MODE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.SERVICE_STATE" />
+ <protected-broadcast android:name="android.intent.action.RADIO_TECHNOLOGY" />
+ <protected-broadcast android:name="android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.EMERGENCY_CALL_STATE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.SIG_STR" />
+ <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+ <protected-broadcast android:name="android.intent.action.DATA_STALL_DETECTED" />
+ <protected-broadcast android:name="android.intent.action.SIM_STATE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.USER_ACTIVITY_NOTIFICATION" />
+ <protected-broadcast android:name="android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS" />
+ <protected-broadcast android:name="android.intent.action.ACTION_MDN_STATE_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SERVICE_PROVIDERS_UPDATED" />
+ <protected-broadcast android:name="android.provider.Telephony.SIM_FULL" />
+ <protected-broadcast android:name="com.android.internal.telephony.carrier_key_download_alarm" />
+ <protected-broadcast android:name="com.android.internal.telephony.data-restart-trysetup" />
+ <protected-broadcast android:name="com.android.internal.telephony.data-stall" />
+ <protected-broadcast android:name="com.android.internal.telephony.provisioning_apn_alarm" />
+ <protected-broadcast android:name="android.intent.action.DATA_SMS_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_DELIVER" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_REJECTED" />
+ <protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
+ <protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_CB_RECEIVED" />
+ <protected-broadcast android:name="android.provider.action.SMS_EMERGENCY_CB_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SECRET_CODE" />
+ <protected-broadcast android:name="com.android.internal.stk.command" />
+ <protected-broadcast android:name="com.android.internal.stk.session_end" />
+ <protected-broadcast android:name="com.android.internal.stk.icc_status_change" />
+ <protected-broadcast android:name="com.android.internal.stk.alpha_notify" />
+ <protected-broadcast android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
+ <protected-broadcast android:name="com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED" />
+ <protected-broadcast android:name="com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE" />
+ <protected-broadcast android:name="com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
+ <protected-broadcast android:name="com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
+ <protected-broadcast android:name="com.android.internal.telephony.PROVISION" />
+ <protected-broadcast android:name="com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED" />
+ <protected-broadcast android:name="com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
+ <protected-broadcast android:name="com.android.intent.isim_refresh" />
+ <protected-broadcast android:name="com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
+ <protected-broadcast android:name="com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE" />
+ <protected-broadcast android:name="com.android.ims.ACTION_RCS_SERVICE_DIED" />
+ <protected-broadcast android:name="com.android.ims.ACTION_PRESENCE_CHANGED" />
+ <protected-broadcast android:name="com.android.ims.ACTION_PUBLISH_STATUS_CHANGED" />
+ <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
+ <protected-broadcast android:name="com.android.ims.IMS_SERVICE_DOWN" />
+ <protected-broadcast android:name="com.android.ims.IMS_INCOMING_CALL" />
+ <protected-broadcast android:name="com.android.ims.internal.uce.UCE_SERVICE_UP" />
+ <protected-broadcast android:name="com.android.ims.internal.uce.UCE_SERVICE_DOWN" />
+ <protected-broadcast android:name="com.android.imsconnection.DISCONNECTED" />
+ <protected-broadcast android:name="com.android.intent.action.IMS_FEATURE_CHANGED" />
+ <protected-broadcast android:name="com.android.intent.action.IMS_CONFIG_CHANGED" />
+ <protected-broadcast android:name="android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR" />
+ <protected-broadcast android:name="com.android.phone.vvm.omtp.sms.REQUEST_SENT" />
+ <protected-broadcast android:name="com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT" />
+ <protected-broadcast android:name="com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED" />
+ <protected-broadcast android:name="com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO" />
+ <protected-broadcast android:name="com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD" />
+ <protected-broadcast android:name="com.android.internal.telephony.action.COUNTRY_OVERRIDE" />
+ <protected-broadcast android:name="com.android.internal.telephony.OPEN_DEFAULT_SMS_APP" />
+ <protected-broadcast android:name="com.android.internal.telephony.ACTION_TEST_OVERRIDE_CARRIER_ID" />
+ <protected-broadcast android:name="android.telephony.action.SIM_CARD_STATE_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.TOGGLE_PROVISION" />
+ <protected-broadcast android:name="android.telephony.action.NETWORK_COUNTRY_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
+ <protected-broadcast android:name="android.telephony.action.CARRIER_SIGNAL_RESET" />
+ <protected-broadcast android:name="android.telephony.action.CARRIER_SIGNAL_PCO_VALUE" />
+ <protected-broadcast android:name="android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
+ <protected-broadcast android:name="android.telephony.action.CARRIER_SIGNAL_REDIRECTED" />
+ <protected-broadcast android:name="android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED" />
+ <protected-broadcast android:name="com.android.phone.settings.CARRIER_PROVISIONING" />
+ <protected-broadcast android:name="com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING" />
+ <protected-broadcast android:name="com.android.internal.telephony.ACTION_VOWIFI_ENABLED" />
+ <protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
+ <protected-broadcast android:name="android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_MANAGED_ROAMING_IND" />
+ <protected-broadcast android:name="android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE" />
+
+ <!-- Added in T -->
+ <protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" />
+ <protected-broadcast android:name="android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED" />
+ <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" />
+ <protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" />
+ <protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" />
+ <protected-broadcast android:name="android.app.action.PROVISIONING_COMPLETED" />
+ <protected-broadcast android:name="android.app.action.LOST_MODE_LOCATION_UPDATE" />
+
+ <!-- Added in U -->
+ <protected-broadcast android:name="android.intent.action.PROFILE_ADDED" />
+ <protected-broadcast android:name="android.intent.action.PROFILE_REMOVED" />
+ <protected-broadcast android:name="com.android.internal.telephony.cat.SMS_SENT_ACTION" />
+ <protected-broadcast android:name="com.android.internal.telephony.cat.SMS_DELIVERY_ACTION" />
+ <protected-broadcast android:name="com.android.internal.telephony.data.ACTION_RETRY" />
+ <protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" />
+ <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW" />
+ <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW" />
+ <protected-broadcast android:name="android.app.admin.action.DEVICE_FINANCING_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.admin.action.DEVICE_POLICY_SET_RESULT" />
+ <protected-broadcast android:name="android.app.admin.action.DEVICE_POLICY_CHANGED" />
+
+ <!-- Added in V -->
+ <protected-broadcast android:name="android.intent.action.PROFILE_AVAILABLE" />
+ <protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" />
+ <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />
+
+ <!-- ====================================================================== -->
+ <!-- RUNTIME PERMISSIONS -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Grouping for platform runtime permissions is not accessible to apps
+ @hide
+ @SystemApi
+ @TestApi
+ -->
+ <permission-group android:name="android.permission-group.UNDEFINED"
+ android:priority="100" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing user's contacts including personal profile -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for runtime permissions related to contacts and profiles on this
+ device. -->
+ <permission-group android:name="android.permission-group.CONTACTS"
+ android:icon="@drawable/perm_group_contacts"
+ android:label="@string/permgrouplab_contacts"
+ android:description="@string/permgroupdesc_contacts"
+ android:priority="100" />
+
+ <!-- Allows an application to read the user's contacts data.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.READ_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readContacts"
+ android:description="@string/permdesc_readContacts"
+ android:protectionLevel="dangerous" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ <!-- Allows an application to write the user's contacts data.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.WRITE_CONTACTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_writeContacts"
+ android:description="@string/permdesc_writeContacts"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an app to update the verification status of E2EE contact keys owned by other apps.
+ <p>This permission is only granted to system apps.
+ <p>Protection level: signature|privileged
+ @SystemApi
+ @hide
+ @FlaggedApi("android.provider.user_keys")
+ -->
+ <permission android:name="android.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_writeVerificationStateE2eeContactKeys"
+ android:description="@string/permdesc_writeVerificationStateE2eeContactKeys"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.provider.user_keys" />
+
+ <!-- Allows an application to set default account for new contacts.
+ <p> This permission is only granted to system applications fulfilling the Contacts app role.
+ <p>Protection level: internal|role
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"
+ android:protectionLevel="internal|role" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing user's calendar -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for runtime permissions related to user's calendar. -->
+ <permission-group android:name="android.permission-group.CALENDAR"
+ android:icon="@drawable/perm_group_calendar"
+ android:label="@string/permgrouplab_calendar"
+ android:description="@string/permgroupdesc_calendar"
+ android:priority="200" />
+
+ <!-- Allows an application to read the user's calendar data.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.READ_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readCalendar"
+ android:description="@string/permdesc_readCalendar"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to write the user's calendar data.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.WRITE_CALENDAR"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_writeCalendar"
+ android:description="@string/permdesc_writeCalendar"
+ android:protectionLevel="dangerous" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing and modifying user's SMS messages -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Allows accessing the messages on ICC
+ @hide Used internally. -->
+ <permission android:name="android.permission.ACCESS_MESSAGES_ON_ICC"
+ android:protectionLevel="signature" />
+
+ <!-- Used for runtime permissions related to user's SMS messages. -->
+ <permission-group android:name="android.permission-group.SMS"
+ android:icon="@drawable/perm_group_sms"
+ android:label="@string/permgrouplab_sms"
+ android:description="@string/permgroupdesc_sms"
+ android:priority="300" />
+
+ <!-- Allows an application to send SMS messages.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.SEND_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_sendSms"
+ android:description="@string/permdesc_sendSms"
+ android:permissionFlags="costsMoney|hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to receive SMS messages.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.RECEIVE_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_receiveSms"
+ android:description="@string/permdesc_receiveSms"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to read SMS messages.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.READ_SMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readSms"
+ android:description="@string/permdesc_readSms"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to receive WAP push messages.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.RECEIVE_WAP_PUSH"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_receiveWapPush"
+ android:description="@string/permdesc_receiveWapPush"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to monitor incoming MMS messages.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.RECEIVE_MMS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_receiveMms"
+ android:description="@string/permdesc_receiveMms"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- @SystemApi @TestApi Allows an application to forward cell broadcast messages to the cell
+ broadcast module. This is required in order to bind to the cell broadcast service, and
+ ensures that only the system can forward messages to it.
+
+ <p>Protection level: signature
+
+ @hide -->
+ <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE"
+ android:label="@string/permlab_bindCellBroadcastService"
+ android:description="@string/permdesc_bindCellBroadcastService"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
+ messages and to register a content observer to get notifications when
+ a cell broadcast has been received and added to the database. For
+ emergency alerts, the database is updated immediately after the
+ alert dialog and notification sound/vibration/speech are presented.
+ The "read" column is then updated after the user dismisses the alert.
+ This enables supplementary emergency assistance apps to start loading
+ additional emergency information (if Internet access is available)
+ when the alert is first received, and to delay presenting the info
+ to the user until after the initial alert dialog is dismissed.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+
+ @hide Pending API council approval -->
+ <permission android:name="android.permission.READ_CELL_BROADCASTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readCellBroadcasts"
+ android:description="@string/permdesc_readCellBroadcasts"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- @SystemApi @hide Allows an application to communicate over satellite.
+ Only granted if the application is a system app or privileged app. -->
+ <permission android:name="android.permission.SATELLITE_COMMUNICATION"
+ android:protectionLevel="role|signature|privileged" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing external storage -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for runtime permissions related to the shared external storage. -->
+ <permission-group android:name="android.permission-group.STORAGE"
+ android:icon="@drawable/perm_group_storage"
+ android:label="@string/permgrouplab_storage"
+ android:description="@string/permgroupdesc_storage"
+ android:priority="900" />
+
+ <!-- Allows an application to read from external storage.
+ <p class="note"><strong>Note: </strong>Starting in API level 33, this permission has no
+ effect. If your app accesses other apps' media files, request one or more of these permissions
+ instead: <a href="#READ_MEDIA_IMAGES"><code>READ_MEDIA_IMAGES</code></a>,
+ <a href="#READ_MEDIA_VIDEO"><code>READ_MEDIA_VIDEO</code></a>,
+ <a href="#READ_MEDIA_AUDIO"><code>READ_MEDIA_AUDIO</code></a>. Learn more about the
+ <a href="{@docRoot}training/data-storage/shared/media#storage-permission">storage
+ permissions</a> that are associated with media files.</p>
+
+ <p>This permission is enforced starting in API level 19. Before API level 19, this
+ permission is not enforced and all apps still have access to read from external storage.
+ You can test your app with the permission enforced by enabling <em>Protect USB
+ storage</em> under <b>Developer options</b> in the Settings app on a device running Android
+ 4.1 or higher.</p>
+ <p>Also starting in API level 19, this permission is <em>not</em> required to
+ read or write files in your application-specific directories returned by
+ {@link android.content.Context#getExternalFilesDir} and
+ {@link android.content.Context#getExternalCacheDir}.</p>
+ <p>Starting in API level 29, apps don't need to request this permission to access files in
+ their app-specific directory on external storage, or their own files in the
+ <a href="{@docRoot}reference/android/provider/MediaStore"><code>MediaStore</code></a>. Apps
+ shouldn't request this permission unless they need to access other apps' files in the
+ <code>MediaStore</code>. Read more about these changes in the
+ <a href="{@docRoot}training/data-storage#scoped-storage">scoped storage</a> section of the
+ developer documentation.</p>
+ <p>If <em>both</em> your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+ minSdkVersion}</a> and <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+ grants your app this permission. If you don't need this permission, be sure your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> is 4 or higher.</p>
+
+ <p> This is a soft restricted permission which cannot be held by an app it its
+ full form until the installer on record allowlists the permission.
+ Specifically, if the permission is allowlisted the holder app can access
+ external storage and the visual and aural media collections while if the
+ permission is not allowlisted the holder app can only access to the visual
+ and aural medial collections. Also the permission is immutably restricted
+ meaning that the allowlist state can be specified only at install time and
+ cannot change until the app is installed. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_sdcardRead"
+ android:description="@string/permdesc_sdcardRead"
+ android:permissionFlags="softRestricted|immutablyRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to read audio files from shared storage.
+ <p>Protection level: dangerous -->
+ <permission-group android:name="android.permission-group.READ_MEDIA_AURAL"
+ android:icon="@drawable/perm_group_read_media_aural"
+ android:label="@string/permgrouplab_readMediaAural"
+ android:description="@string/permgroupdesc_readMediaAural"
+ android:priority="950" />
+
+ <!-- Allows an application to read audio files from external storage.
+ <p>This permission is enforced starting in API level
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}. An app which targets
+ {@link android.os.Build.VERSION_CODES#TIRAMISU} or higher and needs to read audio files from
+ external storage must hold this permission; {@link #READ_EXTERNAL_STORAGE} is not required.
+ For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S_V2} or lower, the
+ {@link #READ_EXTERNAL_STORAGE} permission is required, instead, to read audio files.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readMediaAudio"
+ android:description="@string/permdesc_readMediaAudio"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to read image and video files from shared storage.
+ <p>Protection level: dangerous -->
+ <permission-group android:name="android.permission-group.READ_MEDIA_VISUAL"
+ android:icon="@drawable/perm_group_read_media_visual"
+ android:label="@string/permgrouplab_readMediaVisual"
+ android:description="@string/permgroupdesc_readMediaVisual"
+ android:priority="1000" />
+
+ <!-- Allows an application to read video files from external storage.
+ <p>This permission is enforced starting in API level
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}. An app which targets
+ {@link android.os.Build.VERSION_CODES#TIRAMISU} or higher and needs to read video files from
+ external storage must hold this permission; {@link #READ_EXTERNAL_STORAGE} is not required.
+ For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S_V2} or lower, the
+ {@link #READ_EXTERNAL_STORAGE} permission is required, instead, to read video files.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_VIDEO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readMediaVideo"
+ android:description="@string/permdesc_readMediaVideo"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to read image files from external storage.
+ <p>This permission is enforced starting in API level
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}. An app which targets
+ {@link android.os.Build.VERSION_CODES#TIRAMISU} or higher and needs to read image files from
+ external storage must hold this permission; {@link #READ_EXTERNAL_STORAGE} is not required.
+ For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S_V2} or lower, the
+ {@link #READ_EXTERNAL_STORAGE} permission is required, instead, to read image files.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_IMAGES"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readMediaImages"
+ android:description="@string/permdesc_readMediaImages"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to read image or video files from external storage that a user has
+ selected via the permission prompt photo picker. Apps can check this permission to verify that
+ a user has decided to use the photo picker, instead of granting access to
+ {@link #READ_MEDIA_IMAGES} or {@link #READ_MEDIA_VIDEO}. It does not prevent apps from
+ accessing the standard photo picker manually. This permission should be requested alongside
+ {@link #READ_MEDIA_IMAGES} and/or {@link #READ_MEDIA_VIDEO}, depending on which type of media
+ is desired.
+ <p> This permission will be automatically added to an app's manifest if the app requests
+ {@link #READ_MEDIA_IMAGES}, {@link #READ_MEDIA_VIDEO}, or {@link #ACCESS_MEDIA_LOCATION}
+ regardless of target SDK. If an app does not request this permission, then the grant dialog
+ will return `PERMISSION_GRANTED` for {@link #READ_MEDIA_IMAGES} and/or
+ {@link #READ_MEDIA_VIDEO}, but the app will only have access to the media selected by the
+ user. This false grant state will persist until the app goes into the background.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readVisualUserSelect"
+ android:description="@string/permdesc_readVisualUserSelect"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to write to external storage.
+ <p><strong>Note: </strong>If your app targets {@link android.os.Build.VERSION_CODES#R} or
+ higher, this permission has no effect.
+
+ <p>If your app is on a device that runs API level 19 or higher, you don't need to declare
+ this permission to read and write files in your application-specific directories returned
+ by {@link android.content.Context#getExternalFilesDir} and
+ {@link android.content.Context#getExternalCacheDir}.
+
+ <p>Learn more about how to
+ <a href="{@docRoot}training/data-storage/shared/media#update-other-apps-files">modify media
+ files</a> that your app doesn't own, and how to
+ <a href="{@docRoot}training/data-storage/shared/documents-files">modify non-media files</a>
+ that your app doesn't own.
+
+ <p>If your app is a file manager and needs broad access to external storage files, then
+ the system must place your app on an allowlist so that you can successfully request the
+ <a href="#MANAGE_EXTERNAL_STORAGE><code>MANAGE_EXTERNAL_STORAGE</code></a> permission.
+ Learn more about the appropriate use cases for
+ <a href="{@docRoot}training/data-storage/manage-all-files>managing all files on a storage
+ device</a>.
+
+ <p>If <em>both</em> your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+ minSdkVersion}</a> and <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+ grants your app this permission. If you don't need this permission, be sure your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> is 4 or higher.
+ <p>Protection level: dangerous</p>
+ -->
+ <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_sdcardWrite"
+ android:description="@string/permdesc_sdcardWrite"
+ android:permissionFlags="softRestricted|immutablyRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to access any geographic locations persisted in the
+ user's shared collection.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_mediaLocation"
+ android:description="@string/permdesc_mediaLocation"
+ android:protectionLevel="dangerous" />
+
+ <!-- @hide @SystemApi @TestApi
+ Allows an application to modify OBB files visible to other apps. -->
+ <permission android:name="android.permission.WRITE_OBB"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application a broad access to external storage in scoped storage.
+ Intended to be used by few apps that need to manage files on behalf of the users.
+ <p>Protection level: signature|appop|preinstalled -->
+ <permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:protectionLevel="signature|appop|preinstalled" />
+
+ <!-- Allows an application to modify and delete media files on this device or any connected
+ storage device without user confirmation. Applications must already be granted the
+ {@link #READ_EXTERNAL_STORAGE} or {@link #MANAGE_EXTERNAL_STORAGE}} permissions for this
+ permission to take effect.
+ <p>Even if applications are granted this permission, if applications want to modify or
+ delete media files, they also must get the access by calling
+ {@link android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)},
+ {@link android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)}, or
+ {@link android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)}.
+ <p>This permission doesn't give read or write access directly. It only prevents the user
+ confirmation dialog for these requests.
+ <p>If applications are not granted {@link #ACCESS_MEDIA_LOCATION}, the system also pops up
+ the user confirmation dialog for the write request.
+ <p>Protection level: signature|appop|preinstalled -->
+ <permission android:name="android.permission.MANAGE_MEDIA"
+ android:protectionLevel="signature|appop|preinstalled" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the device location -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that allow accessing the device location. -->
+ <permission-group android:name="android.permission-group.LOCATION"
+ android:icon="@drawable/perm_group_location"
+ android:label="@string/permgrouplab_location"
+ android:description="@string/permgroupdesc_location"
+ android:priority="400" />
+
+ <!-- Allows an app to access precise location.
+ Alternatively, you might want {@link #ACCESS_COARSE_LOCATION}.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.ACCESS_FINE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_accessFineLocation"
+ android:description="@string/permdesc_accessFineLocation"
+ android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- Allows an app to access approximate location.
+ Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_accessCoarseLocation"
+ android:description="@string/permdesc_accessCoarseLocation"
+ android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- Allows an app to access location in the background. If you're requesting this permission,
+ you must also request either {@link #ACCESS_COARSE_LOCATION} or
+ {@link #ACCESS_FINE_LOCATION}. Requesting this permission by itself doesn't give you
+ location access.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_accessBackgroundLocation"
+ android:permissionFlags="hardRestricted"
+ android:description="@string/permdesc_accessBackgroundLocation"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- Allows an application (emergency or advanced driver-assistance app) to bypass
+ location settings.
+ <p>Not for use by third-party applications.
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.LOCATION_BYPASS"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the call log -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated telephony features. -->
+ <permission-group android:name="android.permission-group.CALL_LOG"
+ android:icon="@drawable/perm_group_call_log"
+ android:label="@string/permgrouplab_calllog"
+ android:description="@string/permgroupdesc_calllog"
+ android:priority="450" />
+
+ <!-- Allows an application to access the IMS call service: making and
+ modifying a call
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_IMS_CALL_SERVICE"
+ android:label="@string/permlab_accessImsCallService"
+ android:description="@string/permdesc_accessImsCallService"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to perform IMS Single Registration related actions.
+ Only granted if the application is a system app AND is in the Default SMS Role.
+ The permission is revoked when the app is taken out of the Default SMS Role.
+ <p>Protection level: internal|role
+ -->
+ <permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to read the user's call log.
+ <p class="note"><strong>Note:</strong> If your app uses the
+ {@link #READ_CONTACTS} permission and <em>both</em> your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+ minSdkVersion}</a> and <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> values are set to 15 or lower, the system implicitly
+ grants your app this permission. If you don't need this permission, be sure your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> is 16 or higher.</p>
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.READ_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readCallLog"
+ android:description="@string/permdesc_readCallLog"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to write and read the user's call log data.
+ <p class="note"><strong>Note:</strong> If your app uses the
+ {@link #WRITE_CONTACTS} permission and <em>both</em> your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+ minSdkVersion}</a> and <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> values are set to 15 or lower, the system implicitly
+ grants your app this permission. If you don't need this permission, be sure your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> is 16 or higher.</p>
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.WRITE_CALL_LOG"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_writeCallLog"
+ android:description="@string/permdesc_writeCallLog"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to see the number being dialed during an outgoing
+ call with the option to redirect the call to a different number or
+ abort the call altogether.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+
+ @deprecated Applications should use {@link android.telecom.CallRedirectionService} instead
+ of the {@link android.content.Intent#ACTION_NEW_OUTGOING_CALL} broadcast.
+ -->
+ <permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_processOutgoingCalls"
+ android:description="@string/permdesc_processOutgoingCalls"
+ android:permissionFlags="hardRestricted"
+ android:protectionLevel="dangerous" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the device telephony -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated telephony features. -->
+ <permission-group android:name="android.permission-group.PHONE"
+ android:icon="@drawable/perm_group_phone_calls"
+ android:label="@string/permgrouplab_phone"
+ android:description="@string/permgroupdesc_phone"
+ android:priority="500" />
+
+ <!-- Allows read only access to phone state, including the current cellular network information,
+ the status of any ongoing calls, and a list of any {@link android.telecom.PhoneAccount}s
+ registered on the device.
+ <p class="note"><strong>Note:</strong> If <em>both</em> your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+ minSdkVersion}</a> and <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+ grants your app this permission. If you don't need this permission, be sure your <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> is 4 or higher.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.READ_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readPhoneState"
+ android:description="@string/permdesc_readPhoneState"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows read only access to phone state with a non dangerous permission,
+ including the information like cellular network type, software version. -->
+ <permission android:name="android.permission.READ_BASIC_PHONE_STATE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readBasicPhoneState"
+ android:description="@string/permdesc_readBasicPhoneState"
+ android:protectionLevel="normal" />
+
+ <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
+ granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
+ <p>Protection level: dangerous-->
+ <permission android:name="android.permission.READ_PHONE_NUMBERS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readPhoneNumbers"
+ android:description="@string/permdesc_readPhoneNumbers"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- Allows an application to initiate a phone call without going through
+ the Dialer user interface for the user to confirm the call.
+ <p class="note"><b>Note:</b> An app holding this permission can also call carrier MMI
+ codes to change settings such as call forwarding or call waiting preferences.</p>
+ <p>Protection level: dangerous</p>
+ -->
+ <permission android:name="android.permission.CALL_PHONE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:permissionFlags="costsMoney"
+ android:label="@string/permlab_callPhone"
+ android:description="@string/permdesc_callPhone"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to add voicemails into the system.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_addVoicemail"
+ android:description="@string/permdesc_addVoicemail"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to use SIP service.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.USE_SIP"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_use_sip"
+ android:label="@string/permlab_use_sip"
+ android:protectionLevel="dangerous"/>
+
+ <!-- Allows the app to answer an incoming phone call.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.ANSWER_PHONE_CALLS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_answerPhoneCalls"
+ android:description="@string/permdesc_answerPhoneCalls"
+ android:protectionLevel="dangerous|runtime" />
+
+ <!-- Allows a calling application which manages its own calls through the self-managed
+ {@link android.telecom.ConnectionService} APIs. See
+ {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED} for more information on the
+ self-managed ConnectionService APIs.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.MANAGE_OWN_CALLS"
+ android:label="@string/permlab_manageOwnCalls"
+ android:description="@string/permdesc_manageOwnCalls"
+ android:protectionLevel="normal" />
+
+ <!--Allows an app which implements the
+ {@link android.telecom.InCallService InCallService} API to be eligible to be enabled as a
+ calling companion app. This means that the Telecom framework will bind to the app's
+ InCallService implementation when there are calls active. The app can use the InCallService
+ API to view information about calls on the system and control these calls.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.CALL_COMPANION_APP"
+ android:label="@string/permlab_callCompanionApp"
+ android:description="@string/permdesc_callCompanionApp"
+ android:protectionLevel="normal" />
+
+ <!-- Exempt this uid from restrictions to background audio recoding
+ <p>Protection level: signature|privileged
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS"
+ android:label="@string/permlab_exemptFromAudioRecordRestrictions"
+ android:description="@string/permdesc_exemptFromAudioRecordRestrictions"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows a calling app to continue a call which was started in another app. An example is a
+ video calling app that wants to continue a voice call on the user's mobile network.<p>
+ When the handover of a call from one app to another takes place, there are two devices
+ which are involved in the handover; the initiating and receiving devices. The initiating
+ device is where the request to handover the call was started, and the receiving device is
+ where the handover request is confirmed by the other party.<p>
+ This permission protects access to the
+ {@link android.telecom.TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} which
+ the receiving side of the handover uses to accept a handover.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.ACCEPT_HANDOVER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android.label="@string/permlab_acceptHandover"
+ android:description="@string/permdesc_acceptHandovers"
+ android:protectionLevel="dangerous" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the device microphone -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated with accessing
+ microphone audio from the device. Note that phone calls also capture audio
+ but are in a separate (more visible) permission group. -->
+ <permission-group android:name="android.permission-group.MICROPHONE"
+ android:icon="@drawable/perm_group_microphone"
+ android:label="@string/permgrouplab_microphone"
+ android:description="@string/permgroupdesc_microphone"
+ android:priority="600" />
+
+ <!-- Allows an application to record audio.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.RECORD_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_recordAudio"
+ android:description="@string/permdesc_recordAudio"
+ android:backgroundPermission="android.permission.RECORD_BACKGROUND_AUDIO"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- @SystemApi @TestApi Allows an application to record audio while in the background.
+ This permission is not intended to be held by apps.
+ <p>Protection level: internal
+ @hide -->
+ <permission android:name="android.permission.RECORD_BACKGROUND_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_recordBackgroundAudio"
+ android:description="@string/permdesc_recordBackgroundAudio"
+ android:protectionLevel="internal|role" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for activity recognition -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated with activity recognition. -->
+ <permission-group android:name="android.permission-group.ACTIVITY_RECOGNITION"
+ android:icon="@drawable/perm_group_activity_recognition"
+ android:label="@string/permgrouplab_activityRecognition"
+ android:description="@string/permgroupdesc_activityRecognition"
+ android:priority="1000" />
+
+ <!-- Allows an application to recognize physical activity.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.ACTIVITY_RECOGNITION"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_activityRecognition"
+ android:description="@string/permdesc_activityRecognition"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the vendor UCE Service -->
+ <!-- ====================================================================== -->
+
+ <!-- @hide Allows an application to Access UCE-Presence.
+ <p>Protection level: signature|privileged
+ @deprecated Framework should no longer use this permission to access the vendor UCE service
+ using AIDL, it is instead implemented by RcsCapabilityExchangeImplBase
+ -->
+ <permission android:name="android.permission.ACCESS_UCE_PRESENCE_SERVICE"
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- @hide Allows an application to Access UCE-OPTIONS.
+ <p>Protection level: signature|privileged
+ @deprecated Framework should no longer use this permission to access the vendor UCE service
+ using AIDL, it is instead implemented by RcsCapabilityExchangeImplBase
+ -->
+ <permission android:name="android.permission.ACCESS_UCE_OPTIONS_SERVICE"
+ android:permissionGroup="android.permission-group.PHONE"
+ android:protectionLevel="signature|privileged"/>
+
+
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the device camera -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated with accessing
+ camera or capturing images/video from the device. -->
+ <permission-group android:name="android.permission-group.CAMERA"
+ android:icon="@drawable/perm_group_camera"
+ android:label="@string/permgrouplab_camera"
+ android:description="@string/permgroupdesc_camera"
+ android:priority="700" />
+
+ <!-- Required to be able to access the camera device.
+ <p>This will automatically enforce the
+ <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ uses-feature</a> manifest element for <em>all</em> camera features.
+ If you do not require all camera features or can properly operate if a camera
+ is not available, then you must modify your manifest as appropriate in order to
+ install on devices that don't support all camera features.</p>
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_camera"
+ android:description="@string/permdesc_camera"
+ android:backgroundPermission="android.permission.BACKGROUND_CAMERA"
+ android:protectionLevel="dangerous|instant" />
+
+ <!-- Required to be able to discover and connect to nearby Bluetooth devices.
+ <p>Protection level: dangerous -->
+ <permission-group android:name="android.permission-group.NEARBY_DEVICES"
+ android:icon="@drawable/perm_group_nearby_devices"
+ android:label="@string/permgrouplab_nearby_devices"
+ android:description="@string/permgroupdesc_nearby_devices"
+ android:priority="750" />
+
+ <!-- @SystemApi @TestApi Required to be able to access the camera device in the background.
+ This permission is not intended to be held by apps.
+ <p>Protection level: internal
+ @hide -->
+ <permission android:name="android.permission.BACKGROUND_CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_backgroundCamera"
+ android:description="@string/permdesc_backgroundCamera"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
+ system only camera devices.
+ <p>Protection level: system|signature|role
+ @hide -->
+ <permission android:name="android.permission.SYSTEM_CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_systemCamera"
+ android:description="@string/permdesc_systemCamera"
+ android:protectionLevel="system|signature|role" />
+
+ <!-- @SystemApi Allows receiving the camera service notifications when a camera is opened
+ (by a certain application package) or closed.
+ @hide -->
+ <permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_cameraOpenCloseListener"
+ android:description="@string/permdesc_cameraOpenCloseListener"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows camera access by Headless System User 0 when device is running in
+ HSUM Mode.
+ @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission")
+ @hide -->
+ <permission android:name="android.permission.CAMERA_HEADLESS_SYSTEM_USER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_cameraHeadlessSystemUser"
+ android:description="@string/permdesc_cameraHeadlessSystemUser"
+ android:protectionLevel="signature"
+ android:featureFlag="com.android.internal.camera.flags.camera_hsum_permission" />
+
+
+ <!-- @SystemApi Allows camera access of allowlisted driver assistance apps
+ to be controlled separately.
+ <p> Not for use by third-party applications.
+ @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist")
+ @hide
+ -->
+ <permission android:name="android.permission.CAMERA_PRIVACY_ALLOWLIST"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for accessing the device sensors -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated with accessing
+ body or environmental sensors. -->
+ <permission-group android:name="android.permission-group.SENSORS"
+ android:icon="@drawable/perm_group_sensors"
+ android:label="@string/permgrouplab_sensors"
+ android:description="@string/permgroupdesc_sensors"
+ android:priority="800" />
+
+ <!-- Allows an app to access sensor data with a sampling rate greater than 200 Hz.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_highSamplingRateSensors"
+ android:description="@string/permdesc_highSamplingRateSensors"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to access data from sensors that the user uses to
+ measure what is happening inside their body, such as heart rate.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.BODY_SENSORS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_bodySensors"
+ android:description="@string/permdesc_bodySensors"
+ android:backgroundPermission="android.permission.BODY_SENSORS_BACKGROUND"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to access data from sensors that the user uses to measure what is
+ happening inside their body, such as heart rate. If you're requesting this permission, you
+ must also request {@link #BODY_SENSORS}. Requesting this permission by itself doesn't give
+ you Body sensors access.
+ <p>Protection level: dangerous
+
+ <p> This is a hard restricted permission which cannot be held by an app until
+ the installer on record allowlists the permission. For more details see
+ {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}.
+ -->
+ <permission android:name="android.permission.BODY_SENSORS_BACKGROUND"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_bodySensors_background"
+ android:description="@string/permdesc_bodySensors_background"
+ android:protectionLevel="dangerous"
+ android:permissionFlags="hardRestricted" />
+
+ <!-- Allows an app to use fingerprint hardware.
+ <p>Protection level: normal
+ @deprecated Applications should request {@link
+ android.Manifest.permission#USE_BIOMETRIC} instead
+ -->
+ <permission android:name="android.permission.USE_FINGERPRINT"
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_useFingerprint"
+ android:description="@string/permdesc_useFingerprint"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an app to use device supported biometric modalities.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.USE_BIOMETRIC"
+ android:permissionGroup="android.permission-group.SENSORS"
+ android:label="@string/permlab_useBiometric"
+ android:description="@string/permdesc_useBiometric"
+ android:protectionLevel="normal" />
+
+ <!-- ====================================================================== -->
+ <!-- Permissions for posting notifications -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated with posting notifications
+ -->
+ <permission-group android:name="android.permission-group.NOTIFICATIONS"
+ android:icon="@drawable/ic_notifications_alerted"
+ android:label="@string/permgrouplab_notifications"
+ android:description="@string/permgroupdesc_notifications"
+ android:priority="850" />
+
+ <!-- Allows an app to post notifications
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.POST_NOTIFICATIONS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_postNotification"
+ android:description="@string/permdesc_postNotification"
+ android:protectionLevel="dangerous|instant" />
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
+ <!-- ====================================================================== -->
+ <!-- REMOVED PERMISSIONS -->
+ <!-- ====================================================================== -->
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.READ_PROFILE"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.WRITE_PROFILE"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.READ_SOCIAL_STREAM"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.WRITE_SOCIAL_STREAM"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.READ_USER_DICTIONARY"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.WRITE_USER_DICTIONARY"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @SystemApi @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.WRITE_SMS"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.MANAGE_ACCOUNTS"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.USE_CREDENTIALS"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- @hide We need to keep this around for backwards compatibility -->
+ <permission android:name="android.permission.FLASHLIGHT"
+ android:protectionLevel="normal"
+ android:permissionFlags="removed"/>
+
+ <!-- ====================================================================== -->
+ <!-- INSTALL PERMISSIONS -->
+ <!-- ====================================================================== -->
+
+ <!-- ================================== -->
+ <!-- Permissions for accessing messages -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- Allows an application (Phone) to send a request to other applications
+ to handle the respond-via-message action during incoming calls.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to send SMS to premium shortcodes without user permission.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to filter carrier specific sms.
+ @hide -->
+ <permission android:name="android.permission.CARRIER_FILTER_SMS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
+ to record or display them to the user.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to monitor incoming Bluetooth MAP messages, to record
+ or perform processing on them. -->
+ <!-- @hide -->
+ <permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to execute contacts directory search.
+ This should only be used by ContactsProvider.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to modify the cell broadcasts configuration
+ (i.e. enable or disable channels).
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MODIFY_CELL_BROADCASTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- =============================================================== -->
+ <!-- Permissions for setting the device alarm -->
+ <!-- =============================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to broadcast an Intent to set an alarm for the user.
+ <p>Protection level: normal
+ -->
+ <permission android:name="com.android.alarm.permission.SET_ALARM"
+ android:label="@string/permlab_setAlarm"
+ android:description="@string/permdesc_setAlarm"
+ android:protectionLevel="normal" />
+
+ <!-- =============================================================== -->
+ <!-- Permissions for accessing the user voicemail -->
+ <!-- =============================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to modify and remove existing voicemails in the system.
+ <p>Protection level: signature|privileged|role
+ -->
+ <permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows an application to read voicemails in the system.
+ <p>Protection level: signature|privileged|role
+ -->
+ <permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- ======================================= -->
+ <!-- Permissions for accessing location info -->
+ <!-- ======================================= -->
+ <eat-comment />
+
+ <!-- Allows an application to access extra location provider commands.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
+ android:label="@string/permlab_accessLocationExtraCommands"
+ android:description="@string/permdesc_accessLocationExtraCommands"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to install a location provider into the Location Manager.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to provide location-based time zone suggestions to
+ the system server. This is needed because the system server discovers time zone providers
+ by exposed intent actions and metadata, without it any app could potentially register
+ itself as time zone provider. The system server checks for this permission.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to bind to a android.service.TimeZoneProviderService
+ for the purpose of detecting the device's time zone. This prevents arbitrary clients
+ connecting to the time zone provider service. The system server checks that the provider's
+ intent service explicitly sets this permission via the android:permission attribute of the
+ service.
+ This is only expected to be possessed by the system server outside of tests.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files.
+ This should only be used by HDMI-CEC service.
+ -->
+ <permission android:name="android.permission.HDMI_CEC"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- Allows an application to use location features in hardware,
+ such as the geofencing api.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.LOCATION_HARDWARE"
+ android:protectionLevel="signature|privileged|role" />
+ <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+
+ <!-- @SystemApi Allows an application to use the Context Hub.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_CONTEXT_HUB"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB"/>
+
+ <!-- @SystemApi Allows an application to create mock location providers for testing.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_MOCK_LOCATION"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+ Allows automotive applications to control location
+ suspend state for power management use cases.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.CONTROL_AUTOMOTIVE_GNSS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- ======================================= -->
+ <!-- Permissions for accessing networks -->
+ <!-- ======================================= -->
+ <eat-comment />
+
+ <!-- Allows applications to open network sockets.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.INTERNET"
+ android:description="@string/permdesc_createNetworkSockets"
+ android:label="@string/permlab_createNetworkSockets"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows applications to access information about networks.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.ACCESS_NETWORK_STATE"
+ android:description="@string/permdesc_accessNetworkState"
+ android:label="@string/permlab_accessNetworkState"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows applications to access information about Wi-Fi networks.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.ACCESS_WIFI_STATE"
+ android:description="@string/permdesc_accessWifiState"
+ android:label="@string/permlab_accessWifiState"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to change Wi-Fi connectivity state.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.CHANGE_WIFI_STATE"
+ android:description="@string/permdesc_changeWifiState"
+ android:label="@string/permlab_changeWifiState"
+ android:protectionLevel="normal" />
+
+ <!-- This permission is used to let OEMs grant their trusted app access to a subset of
+ privileged wifi APIs to improve wifi performance. Allows applications to manage
+ Wi-Fi network selection related features such as enable or disable global auto-join,
+ modify connectivity scan intervals, and approve Wi-Fi Direct connections.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_WIFI_NETWORK_SELECTION"
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
+
+ <!-- Allows applications to get notified when a Wi-Fi interface request cannot
+ be satisfied without tearing down one or more other interfaces, and provide a decision
+ whether to approve the request or reject it.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_WIFI_INTERFACES"
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
+
+ <!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
+ <p>Only granted to applications that are currently bound by the
+ system for creating and managing IPsec-based interfaces.
+ -->
+ <permission android:name="android.permission.MANAGE_IPSEC_TUNNELS"
+ android:protectionLevel="signature|appop" />
+
+ <!-- @SystemApi @hide Allows apps to create and manage Test Networks.
+ <p>Granted only to shell. CTS tests will use
+ UiAutomation.AdoptShellPermissionIdentity() to gain access.
+ -->
+ <permission android:name="android.permission.MANAGE_TEST_NETWORKS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <RemoteAuth>Service interfaces.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_REMOTE_AUTH"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <RemoteAuth>Service authentication methods.
+ @hide -->
+ <permission android:name="android.permission.USE_REMOTE_AUTH"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_WIFI_CREDENTIAL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows applications to change tether state and run
+ tether carrier provisioning.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.TETHER_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allow system apps to receive broadcast
+ when a wifi network credential is changed.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to modify any wifi configuration, even if created
+ by another application. Once reconfigured the original creator cannot make any further
+ modifications.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
+
+ <!-- @deprecated Allows applications to act as network scorers. @hide @SystemApi-->
+ <permission android:name="android.permission.SCORE_NETWORKS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @deprecated Allows applications to request network
+ recommendations and scores from the NetworkScoreService.
+ @SystemApi
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
+ android:protectionLevel="signature|setup" />
+
+ <!-- Allows applications to restart the Wi-Fi subsystem.
+ @SystemApi
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows applications to toggle airplane mode.
+ <p>Not for use by third-party or privileged applications.
+ -->
+ <permission android:name="android.permission.NETWORK_AIRPLANE_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows network stack services (Connectivity and Wifi) to coordinate
+ <p>Not for use by third-party or privileged applications.
+ @SystemApi @TestApi
+ @hide This should only be used by Connectivity and Wifi Services.
+ -->
+ <permission android:name="android.permission.NETWORK_STACK"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to observe network policy changes. -->
+ <permission android:name="android.permission.OBSERVE_NETWORK_POLICY"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows applications to register network factory or agent -->
+ <permission android:name="android.permission.NETWORK_FACTORY"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi @hide Allows applications to access network stats provider -->
+ <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
+ android:protectionLevel="signature" />
+
+ <!-- Allows Settings and SystemUI to call methods in Networking services
+ <p>Not for use by third-party or privileged applications.
+ @SystemApi @TestApi
+ @hide This should only be used by Settings and SystemUI.
+ -->
+ <permission android:name="android.permission.NETWORK_SETTINGS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows holder to request bluetooth/wifi scan bypassing global "use location" setting and
+ location permissions.
+ <p>Not for use by third-party or privileged applications.
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"
+ android:protectionLevel="signature|companion" />
+
+ <!-- Allows SetupWizard to call methods in Networking services
+ <p>Not for use by any other third-party or privileged applications.
+ @SystemApi
+ @hide This should only be used by SetupWizard.
+ -->
+ <permission android:name="android.permission.NETWORK_SETUP_WIZARD"
+ android:protectionLevel="signature|setup" />
+
+ <!-- Allows Managed Provisioning to call methods in Networking services
+ <p>Not for use by any other third-party or privileged applications.
+ @SystemApi
+ @hide This should only be used by ManagedProvisioning app.
+ -->
+ <permission android:name="android.permission.NETWORK_MANAGED_PROVISIONING"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows Carrier Provisioning to call methods in Networking services
+ <p>Not for use by any other third-party or privileged applications.
+ @SystemApi
+ @hide This should only be used by CarrierProvisioning.
+ -->
+ <permission android:name="android.permission.NETWORK_CARRIER_PROVISIONING"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.ACCESS_LOWPAN_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- #SystemApi @hide Allows applications to change LoWPAN connectivity state.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_LOWPAN_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- #SystemApi @hide Allows applications to read LoWPAN credential.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- #SystemApi @hide Allows a service to register or unregister
+ new LoWPAN interfaces.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows changing Thread network state and access to Thread network
+ credentials such as Network Key and PSKc.
+ <p>Not for use by third-party applications.
+ @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") -->
+ <permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows access to Thread network APIs or shell commands ("cmd thread_network") which
+ are only for testing. -->
+ <permission android:name="android.permission.THREAD_NETWORK_TESTING"
+ android:protectionLevel="signature" />
+
+ <!-- #SystemApi @hide Allows an app to bypass Private DNS.
+ <p>Not for use by third-party applications.
+ TODO: publish as system API in next API release. -->
+ <permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can
+ be increased when the device is stationary in order to save power.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows applications to update Wifi/Cellular coex channels to avoid.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi @hide Allows applications to access Wifi/Cellular coex channels being avoided.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi @hide Allows system APK to manage country code.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_WIFI_COUNTRY_CODE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to manage an automotive device's application network
+ preference as it relates to OEM_PAID and OEM_PRIVATE capable networks.
+ <p>Not for use by third-party or privileged applications. -->
+ <permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to manage ethernet networks.
+ <p>Not for use by third-party or privileged applications. -->
+ <permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows system apps to call methods to register itself as a mDNS offload engine.
+ <p>Not for use by third-party or privileged applications.
+ @SystemApi
+ @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine")
+ @hide This should only be used by system apps.
+ -->
+ <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
+ android:protectionLevel="signature"
+ android:featureFlag="android.net.platform.flags.register_nsd_offload_engine" />
+
+ <!-- ======================================= -->
+ <!-- Permissions for short range, peripheral networks -->
+ <!-- ======================================= -->
+ <eat-comment />
+
+ <!-- Allows applications to connect to paired bluetooth devices.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.BLUETOOTH"
+ android:description="@string/permdesc_bluetooth"
+ android:label="@string/permlab_bluetooth"
+ android:protectionLevel="normal" />
+
+ <!-- Required to be able to discover and pair nearby Bluetooth devices.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.BLUETOOTH_SCAN"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_bluetooth_scan"
+ android:label="@string/permlab_bluetooth_scan"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to connect to paired Bluetooth devices.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.BLUETOOTH_CONNECT"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_bluetooth_connect"
+ android:label="@string/permlab_bluetooth_connect"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to advertise to nearby Bluetooth devices.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.BLUETOOTH_ADVERTISE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_bluetooth_advertise"
+ android:label="@string/permlab_bluetooth_advertise"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to range to devices using ultra-wideband.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.UWB_RANGING"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_uwb_ranging"
+ android:label="@string/permlab_uwb_ranging"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to advertise and connect to nearby devices via Wi-Fi.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.NEARBY_WIFI_DEVICES"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:description="@string/permdesc_nearby_wifi_devices"
+ android:label="@string/permlab_nearby_wifi_devices"
+ android:protectionLevel="dangerous" />
+
+ <!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the
+ user from using them until they are unsuspended.
+ @hide
+ -->
+ <permission android:name="android.permission.SUSPEND_APPS"
+ android:protectionLevel="signature|role|verifier" />
+
+ <!-- @SystemApi
+ @hide
+ @FlaggedApi("android.content.pm.quarantined_enabled")
+ Allows an application to quarantine other apps, which will prevent
+ them from running without explicit user action.
+ -->
+ <permission android:name="android.permission.QUARANTINE_APPS"
+ android:protectionLevel="signature|verifier"
+ android:featureFlag="android.content.pm.quarantined_enabled" />
+
+ <!-- Allows applications to discover and pair bluetooth devices.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.BLUETOOTH_ADMIN"
+ android:description="@string/permdesc_bluetoothAdmin"
+ android:label="@string/permlab_bluetoothAdmin"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to pair bluetooth devices without user interaction, and to
+ allow or disallow phonebook access or message access.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Control access to email providers exclusively for Bluetooth
+ @hide
+ -->
+ <permission android:name="android.permission.BLUETOOTH_MAP"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows bluetooth stack to access files
+ This should only be granted to the Bluetooth apk.
+ @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+ -->
+ <permission android:name="android.permission.BLUETOOTH_STACK"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows uhid write access for creating virtual input devices
+ @hide
+ -->
+ <permission android:name="android.permission.VIRTUAL_INPUT_DEVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows applications to perform I/O operations over NFC.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.NFC"
+ android:description="@string/permdesc_nfc"
+ android:label="@string/permlab_nfc"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to receive NFC transaction events.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.NFC_TRANSACTION_EVENT"
+ android:description="@string/permdesc_nfcTransactionEvent"
+ android:label="@string/permlab_nfcTransactionEvent"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to receive NFC preferred payment service information.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.NFC_PREFERRED_PAYMENT_INFO"
+ android:description="@string/permdesc_preferredPaymentInfo"
+ android:label="@string/permlab_preferredPaymentInfo"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi Allows access to set NFC controller always on states.
+ <p>Protection level: signature|privileged
+ @hide -->
+ <permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+ Applications holding this permission can access OMAPI reset system API
+ and bypass OMAPI AccessControlEnforcer.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @deprecated This permission used to allow too broad access to sensitive methods and all its
+ uses have been replaced by a more appropriate permission. Most uses have been replaced with
+ a NETWORK_STACK or NETWORK_SETTINGS check. Please look up the documentation of the
+ individual functions to figure out what permission now protects the individual function.
+ @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
+ @hide -->
+ <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an internal user to use restricted Networks.
+ @hide -->
+ <permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+
+ <!-- @SystemApi Allows an internal user to set signal strength in NetworkRequest. This kind of
+ request will wake up device when signal strength meets the given value.
+ @hide -->
+ <permission android:name="android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows a system application to access hardware packet offload capabilities.
+ @hide -->
+ <permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows access to the loop radio (Android@Home mesh network) device.
+ @hide -->
+ <permission android:name="android.permission.LOOP_RADIO"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows sending and receiving handover transfer status from Wifi and Bluetooth
+ @hide -->
+ <permission android:name="android.permission.NFC_HANDOVER_STATUS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows internal management of Bluetooth state when on wireless consent mode.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows the device to be reset, clearing all data and enables Test Harness Mode. -->
+ <permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows access to ultra wideband device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.UWB_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- ================================== -->
+ <!-- Permissions for accessing accounts -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- Allows access to the list of accounts in the Accounts Service.
+
+ <p class="note"><strong>Note:</strong> Beginning with Android 6.0 (API level
+ 23), if an app shares the signature of the authenticator that manages an
+ account, it does not need <code>"GET_ACCOUNTS"</code> permission to read
+ information about that account. On Android 5.1 and lower, all apps need
+ <code>"GET_ACCOUNTS"</code> permission to read information about any
+ account.</p>
+
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.GET_ACCOUNTS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:protectionLevel="dangerous"
+ android:description="@string/permdesc_getAccounts"
+ android:label="@string/permlab_getAccounts" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+
+ <!-- Allows applications to call into AccountAuthenticators.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.ACCOUNT_MANAGER"
+ android:protectionLevel="signature" />
+
+ <!-- ================================== -->
+ <!-- Permissions for accessing hardware that may effect battery life-->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- Allows applications to enter Wi-Fi Multicast mode.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
+ android:description="@string/permdesc_changeWifiMulticastState"
+ android:label="@string/permlab_changeWifiMulticastState"
+ android:protectionLevel="normal" />
+
+ <!-- Allows access to the vibrator.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.VIBRATE"
+ android:label="@string/permlab_vibrate"
+ android:description="@string/permdesc_vibrate"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows access to the vibrator always-on settings.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.VIBRATE_ALWAYS_ON"
+ android:protectionLevel="signature" />
+
+ <!-- Allows access to system-only haptic feedback constants.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows access to the vibrator state.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_VIBRATOR_STATE"
+ android:label="@string/permdesc_vibrator_state"
+ android:description="@string/permdesc_vibrator_state"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
+ from dimming.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.WAKE_LOCK"
+ android:label="@string/permlab_wakeLock"
+ android:description="@string/permdesc_wakeLock"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows using the device's IR transmitter, if available.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.TRANSMIT_IR"
+ android:label="@string/permlab_transmitIr"
+ android:description="@string/permdesc_transmitIr"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an app to turn on the screen on, e.g. with
+ {@link android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP}.
+ <p>Intended to only be used by home automation apps.
+ -->
+ <permission android:name="android.permission.TURN_SCREEN_ON"
+ android:label="@string/permlab_turnScreenOn"
+ android:description="@string/permdesc_turnScreenOn"
+ android:protectionLevel="signature|privileged|appop" />
+
+ <!-- ==================================================== -->
+ <!-- Permissions related to changing audio settings -->
+ <!-- ==================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to modify global audio settings.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"
+ android:label="@string/permlab_modifyAudioSettings"
+ android:description="@string/permdesc_modifyAudioSettings"
+ android:protectionLevel="normal" />
+
+ <!-- ==================================================== -->
+ <!-- Permissions related to screen capture -->
+ <!-- ==================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to capture screen content to perform a screenshot using the intent
+ action {@link android.content.Intent#ACTION_LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE}.
+ <p>Protection level: internal|role
+ <p>Intended for use by ROLE_NOTES only.
+ -->
+ <permission android:name="android.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to get notified when a screen capture of its windows is attempted.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.DETECT_SCREEN_CAPTURE"
+ android:label="@string/permlab_detectScreenCapture"
+ android:description="@string/permdesc_detectScreenCapture"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to get notified when it is being recorded.
+ <p>Protection level: normal
+ @FlaggedApi("com.android.window.flags.screen_recording_callbacks")
+ -->
+ <permission android:name="android.permission.DETECT_SCREEN_RECORDING"
+ android:protectionLevel="normal"
+ android:featureFlag="com.android.window.flags.screen_recording_callbacks" />
+
+ <!-- ======================================== -->
+ <!-- Permissions for factory reset protection -->
+ <!-- ======================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows an application to set a factory reset protection (FRP) policy.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_FACTORY_RESET_PROTECTION"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- ======================================== -->
+ <!-- Permissions for lost mode -->
+ <!-- ======================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows an application to trigger lost mode on an organization-owned device.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.TRIGGER_LOST_MODE"
+ android:protectionLevel="signature|role"/>
+
+ <!-- ================================== -->
+ <!-- Permissions for accessing hardware -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows an application to manage preferences and permissions for USB devices
+ @hide -->
+ <permission android:name="android.permission.MANAGE_USB"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to manage Android Debug Bridge settings.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_DEBUGGING"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to access the MTP USB kernel driver.
+ For use only by the device side MTP implementation.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_MTP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows access to hardware peripherals. Intended only for hardware testing.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.HARDWARE_TEST"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to manage DynamicSystem image -->
+ <permission android:name="android.permission.MANAGE_DYNAMIC_SYSTEM"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to install a DynamicSystem image and get status updates.
+ @hide -->
+ <permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows access to Broadcast Radio
+ @hide This is not a third-party API (intended for system apps).-->
+ <permission android:name="android.permission.ACCESS_BROADCAST_RADIO"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @deprecated @SystemApi Allows access to FM
+ @hide This is not a third-party API (intended for system apps).-->
+ <permission android:name="android.permission.ACCESS_FM_RADIO"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows access to configure network interfaces, configure/use IPSec, etc.
+ @hide -->
+ <permission android:name="android.permission.NET_ADMIN"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows registration for remote audio playback. @hide -->
+ <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
+ android:protectionLevel="signature" />
+
+ <!-- Allows TvInputService to access underlying TV input hardware such as
+ built-in tuners and HDMI-in's.
+ <p>This should only be used by OEM's TvInputService's.
+ @hide @SystemApi -->
+ <permission android:name="android.permission.TV_INPUT_HARDWARE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- Allows to capture a frame of TV input hardware such as
+ built-in tuners and HDMI-in's.
+ <p>Not for use by third-party applications.
+ @hide @SystemApi -->
+ <permission android:name="android.permission.CAPTURE_TV_INPUT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows TvInputService to access DVB device.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.DVB_DEVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows reading and enabling/disabling the OEM unlock allowed by carrier state
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows reading and enabling/disabling the OEM unlock allowed by user state
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows reading the OEM unlock state
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_OEM_UNLOCK_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows enabling/disabling OEM unlock
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.OEM_UNLOCK_STATE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows configuration of factory reset protection
+ @FlaggedApi("android.security.frp_enforcement")
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CONFIGURE_FACTORY_RESET_PROTECTION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows querying state of PersistentDataBlock
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.ACCESS_PDB_STATE"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows testing if a passwords is forbidden by the admins.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.TEST_BLACKLISTED_PASSWORD"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows system update service to notify device owner about pending updates.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- =========================================== -->
+ <!-- Permissions associated with camera and image capture -->
+ <!-- =========================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows disabling the transmit-indicator LED that is normally on when
+ a camera is in use by an application.
+ @hide -->
+ <permission android:name="android.permission.CAMERA_DISABLE_TRANSMIT_LED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows sending the camera service notifications about system-wide events.
+ @hide -->
+ <permission android:name="android.permission.CAMERA_SEND_SYSTEM_EVENTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows injecting the external camera to replace the internal camera.
+ @hide -->
+ <permission android:name="android.permission.CAMERA_INJECT_EXTERNAL_CAMERA"
+ android:protectionLevel="signature" />
+
+ <!-- =========================================== -->
+ <!-- Permissions associated with telephony state -->
+ <!-- =========================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi Allows granting runtime permissions to telephony related components.
+ @hide -->
+ <permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows modification of the telephony state - power on, mmi, etc.
+ Does not include placing calls.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MODIFY_PHONE_STATE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows read only access to precise phone state.
+ Allows reading of detailed information about phone state for special-use applications
+ such as dialers, carrier applications, or ims applications. -->
+ <permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @TestApi Allows read access to privileged phone state.
+ @hide Used internally. -->
+ <permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows to read device identifiers and use ICC based authentication like EAP-AKA.
+ Often required in authentication to access the carrier's server and manage services
+ of the subscriber.
+ <p>Protection level: signature|appop -->
+ <permission android:name="android.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER"
+ android:protectionLevel="signature|appop" />
+
+ <!-- @SystemApi Allows read access to emergency number information for ongoing calls or SMS
+ sessions.
+ @hide Used internally. -->
+ <permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"
+ android:protectionLevel="signature" />
+
+ <!-- Allows listen permission to always reported system signal strength.
+ @hide Used internally. -->
+ <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Protects the ability to register any PhoneAccount with
+ PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
+ corresponds to a device SIM.
+ @hide -->
+ <permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Protects the ability to register any PhoneAccount with
+ PhoneAccount#CAPABILITY_CALL_PROVIDER.
+ @hide -->
+ <permission android:name="android.permission.REGISTER_CALL_PROVIDER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Protects the ability to register any PhoneAccount with
+ PhoneAccount#CAPABILITY_CONNECTION_MANAGER
+ @hide -->
+ <permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.telecom.InCallService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_INCALL_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.telecom.CallStreamingService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @SystemApi @hide-->
+ <permission android:name="android.permission.BIND_CALL_STREAMING_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows to query ongoing call details and manage ongoing calls
+ <p>Protection level: signature|appop -->
+ <permission android:name="android.permission.MANAGE_ONGOING_CALLS"
+ android:protectionLevel="signature|appop"
+ android:label="@string/permlab_manageOngoingCalls"
+ android:description="@string/permdesc_manageOngoingCalls" />
+
+ <!-- Allows the app to request network scans from telephony.
+ <p>Not for use by third-party applications.
+ @SystemApi @hide-->
+ <permission android:name="android.permission.NETWORK_SCAN"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a link {@link android.telephony.VisualVoicemailService} to ensure that
+ only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission
+ android:name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- Must be required by a {@link android.telecom.CallScreeningService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_SCREENING_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.telecom.PhoneAccountSuggestionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.telecom.CallDiagnosticService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_CALL_DIAGNOSTIC_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.telecom.CallRedirectionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.telecom.ConnectionService},
+ to ensure that only the system can bind to it.
+ @deprecated {@link android.telecom.ConnectionService}s should require
+ android.permission.BIND_TELECOM_CONNECTION_SERVICE instead.
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.BIND_CONNECTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.telecom.ConnectionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to control the in-call experience.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows an application to receive STK related commands.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_STK_COMMANDS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to send EMBMS download intents to apps
+ @hide -->
+ <permission android:name="android.permission.SEND_EMBMS_INTENTS"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- Allows internal management of the sensor framework
+ @hide -->
+ <permission android:name="android.permission.MANAGE_SENSORS"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a DomainSelectionService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service")
+ -->
+ <permission android:name="android.permission.BIND_DOMAIN_SELECTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an ImsService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_IMS_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- Must be required by a SatelliteService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_SATELLITE_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- Must be required by a SatelliteGatewayService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a telephony data service to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a NetworkService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to manage embedded subscriptions (those on a eUICC)
+ through EuiccManager APIs.
+ <p>Protection level: signature|privileged|development
+ @hide
+ -->
+ <permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @SystemApi Must be required by an EuiccService to ensure that only the system can bind to
+ it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_EUICC_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Required for reading information about carrier apps from SystemConfigManager.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.READ_CARRIER_APP_INFO"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an GbaService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_GBA_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Required for an Application to access APIs related to RCS User Capability Exchange.
+ <p> This permission is only granted to system applications fulfilling the SMS, Dialer, and
+ Contacts app roles.
+ <p>Protection level: internal|role
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Used to provide the Telecom framework with access to the last known call ID.
+ <p>Protection level: signature
+ @SystemApi
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_LAST_KNOWN_CELL_ID"
+ android:protectionLevel="signature"
+ android:label="@string/permlab_accessLastKnownCellId"
+ android:description="@string/permdesc_accessLastKnownCellId"/>
+
+ <!-- ================================== -->
+ <!-- Permissions for sdcard interaction -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- @SystemApi @TestApi Allows an application to write to internal media storage
+ @deprecated This permission is no longer honored in the system and no longer adds
+ the media_rw gid as a supplementary gid to the holder. Use the
+ android.permission.MANAGE_EXTERNAL_STORAGE instead.
+ @hide -->
+ <permission android:name="android.permission.WRITE_MEDIA_STORAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to manage access to documents, usually as part
+ of a document picker.
+ <p>This permission should <em>only</em> be requested by the platform
+ document management app. This permission cannot be granted to
+ third-party apps.
+ -->
+ <permission android:name="android.permission.MANAGE_DOCUMENTS"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows an application to manage access to crates, usually as part
+ of a crates picker.
+ <p>This permission should <em>only</em> be requested by the platform
+ management app. This permission cannot be granted to
+ third-party apps.
+ @hide
+ @TestApi
+ -->
+ <permission android:name="android.permission.MANAGE_CRATES"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to cache content.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.CACHE_CONTENT"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi @hide
+ Allows an application to aggressively allocate disk space.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.ALLOCATE_AGGRESSIVE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide
+ Allows an application to use reserved disk space.
+ <p>Not for use by third-party applications. Should only be requested by
+ apps that provide core system functionality, to ensure system stability
+ when disk is otherwise completely full.
+ -->
+ <permission android:name="android.permission.USE_RESERVED_DISK"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- ================================== -->
+ <!-- Permissions for screenlock -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- Allows applications to disable the keyguard if it is not secure.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.DISABLE_KEYGUARD"
+ android:description="@string/permdesc_disableKeyguard"
+ android:label="@string/permlab_disableKeyguard"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to request the screen lock complexity and prompt users to update the
+ screen lock to a certain complexity level.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_PASSWORD_COMPLEXITY"
+ android:label="@string/permlab_requestPasswordComplexity"
+ android:description="@string/permdesc_requestPasswordComplexity"
+ android:protectionLevel="normal" />
+
+ <!-- ================================== -->
+ <!-- Permissions to access other installed applications -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- @deprecated No longer enforced. -->
+ <permission android:name="android.permission.GET_TASKS"
+ android:label="@string/permlab_getTasks"
+ android:description="@string/permdesc_getTasks"
+ android:protectionLevel="normal" />
+
+ <!-- New version of GET_TASKS that apps can request, since GET_TASKS doesn't really
+ give access to task information. We need this new one because there are
+ many existing apps that use add libraries and such that have validation
+ code to ensure the app has requested the GET_TASKS permission by seeing
+ if it has been granted the permission... if it hasn't, it kills the app
+ with a message about being upset. So we need to have it continue to look
+ like the app is getting that permission, even though it will never be
+ checked, and new privileged apps can now request this one for real access.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.REAL_GET_TASKS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ @hide -->
+ <permission android:name="android.permission.START_TASKS_FROM_RECENTS"
+ android:protectionLevel="signature|privileged|recents" />
+
+ <!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
+ across the users on the device, using singleton services and
+ user-targeted broadcasts. This permission is not available to
+ third party applications. -->
+ <permission android:name="android.permission.INTERACT_ACROSS_USERS"
+ android:protectionLevel="signature|privileged|development|role" />
+
+ <!-- @SystemApi Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+ that removes restrictions on where broadcasts can be sent and allows other
+ types of interactions
+ @hide -->
+ <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
+ android:protectionLevel="signature|installer|module|role" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+
+ <!-- Allows interaction across profiles in the same profile group. -->
+ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
+ android:protectionLevel="signature|appop" />
+
+ <!-- Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property
+ <p>Protection level: normal
+ @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
+ <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES"
+ android:label="@string/permlab_accessHiddenProfile"
+ android:description="@string/permdesc_accessHiddenProfile"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile
+ users.
+ @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
+ <permission
+ android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows starting activities across profiles in the same profile group. -->
+ <permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows configuring apps to have the INTERACT_ACROSS_PROFILES permission so that
+ they can interact across profiles in the same profile group.
+ @hide -->
+ <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
+ users on the device. This permission is not available to
+ third party applications. -->
+ <permission android:name="android.permission.MANAGE_USERS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to create, remove users and get the list of
+ users on the device. Applications holding this permission can create users (including
+ normal, restricted, guest, managed, and demo users) and can optionally endow them with the
+ ephemeral property. For creating users with other kinds of properties,
+ {@link android.Manifest.permission#MANAGE_USERS} is needed.
+ This permission is not available to third party applications. -->
+ <permission android:name="android.permission.CREATE_USERS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to set user association
+ with a certain subscription. Used by Enterprise to associate a
+ subscription with a work or personal profile. -->
+ <permission android:name="android.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to call APIs that allow it to query users on the
+ device. -->
+ <permission android:name="android.permission.QUERY_USERS"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows an application to access data blobs across users. -->
+ <permission android:name="android.permission.ACCESS_BLOBS_ACROSS_USERS"
+ android:protectionLevel="signature|privileged|development|role" />
+
+ <!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
+ This permission is not available to third party applications.-->
+ <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
+ android:protectionLevel="signature|role"
+ android:label="@string/permlab_manageProfileAndDeviceOwners"
+ android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+
+ <!-- @SystemApi @hide Allows an application to query device policies set by any admin on
+ the device.-->
+ <permission android:name="android.permission.QUERY_ADMIN_POLICY"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi @hide Allows an application to exempt apps from platform restrictions.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage device policy relating to time.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set the grant state of runtime permissions on packages.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage the identity of the managing organization.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set support messages for when a user action is affected by an
+ active policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage backup service policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage lock task policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy regarding modifying applications.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage installing from unknown sources policy.
+ <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected
+ by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage application restrictions.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage calling policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage debugging features policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy preventing users from modifying users.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage safe boot policy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to restricting a user's ability to use or
+ enable and disable the microphone.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to restricting a user's ability to use or
+ enable and disable the camera.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to keyguard.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to account management.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to hiding and suspending packages.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to force set a new device unlock password or a managed profile
+ challenge on current user.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to the status bar.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STATUS_BAR"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to bluetooth.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to fun.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FUN"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to airplane mode.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to mobile networks.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to physical media.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to sms.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SMS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to usb file transfers.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to lock credentials.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to Wifi.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to screen capture.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to input methods.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to restricting the user from configuring
+ private DNS.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to the default sms application.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to profiles.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to interacting with profiles (e.g. Disallowing
+ cross-profile copy and paste).
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to VPNs.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_VPN"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to audio output.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to the display.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to location.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCATION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to factory reset.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to the wallpaper.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to the usage of the contents of the screen.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to system dialogs.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to users running in the background.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to printing.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRINTING"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to nearby communications (e.g. Beam and
+ nearby streaming).
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to <a
+ href="https://www.threadgroup.org">Thread</a> network.
+ @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to sending assist content to a
+ privileged app such as the Assistant app.
+ @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to windows.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WINDOWS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to locale.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCALE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to autofill.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to users.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USERS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to certificates.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to override APNs.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_OVERRIDE_APN"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to security logging.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to use audit logging API.
+ @hide
+ @SystemApi
+ @FlaggedApi("android.app.admin.flags.security_log_v2_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to system updates.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application query system updates.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to private DNS.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRIVATE_DNS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to settings.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SETTINGS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to network logging.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NETWORK_LOGGING"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to usb data signalling.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to suspending personal apps.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to keeping uninstalled packages.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
+ required to call APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to accessibility.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCESSIBILITY"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to common criteria mode.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to metered data.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_METERED_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set a network-independent global HTTP proxy.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROXY"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to request bugreports with user consent.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BUGREPORT"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to application user data.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to lock a profile or the device with the appropriate cross-user
+ permission.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to query the device stolen state.
+ @FlaggedApi("android.app.admin.flags.device_theft_api_enabled")
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.QUERY_DEVICE_STOLEN_STATE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to system apps.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to wiping data.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
+ APIs protected by this permission on users different to the calling user.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIPE_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to the Memory Tagging Extension (MTE).
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MTE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to device identifiers. -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to content protection.
+ <p>Protection level: internal|role
+ @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set policy related to subscriptions downloaded by an admin.
+ <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+ APIs protected by this permission on users different to the calling user.
+ @FlaggedApi("android.app.admin.flags.esim_management_enabled") -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to block package uninstallation.
+ @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to camera toggle.
+ @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to microphone toggle.
+ @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set device policies outside the current user
+ that are critical for securing data within the current user.
+ <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
+ permissions across all users on the device provided they are required for securing data
+ within the current user.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set device policies outside the current user
+ that are required for securing device ownership without accessing user data.
+ <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
+ permissions across all users on the device provided they do not grant access to user
+ data. -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to set device policies outside the current user.
+ <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS}
+ that removes the restriction on accessing user data.
+ <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_*
+ permissions across all users on the device.-->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows the holder to manage and retrieve max storage limit for admin policies. This
+ permission is only grantable on rooted devices.
+ @TestAPI
+ @hide -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT"
+ android:protectionLevel="internal" />
+
+ <!-- Allows an application to access EnhancedConfirmationManager.
+ @SystemApi
+ @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled")
+ @hide This is not a third-party API (intended for OEMs and system apps). -->
+ <permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES"
+ android:protectionLevel="signature|installer" />
+ <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
+
+ <!-- @SystemApi @hide Allows an application to set a device owner on retail demo devices.-->
+ <permission android:name="android.permission.PROVISION_DEMO_DEVICE"
+ android:protectionLevel="signature|setup|knownSigner"
+ android:knownCerts="@array/demo_device_provisioning_known_signers" />
+
+ <!-- @TestApi @hide Allows an application to reset the record of previous system update freeze
+ periods. -->
+ <permission android:name="android.permission.CLEAR_FREEZE_PERIOD"
+ android:protectionLevel="signature" />
+
+ <!-- @TestApi @hide Allows an application to force available DevicePolicyManager logs to
+ DPC. -->
+ <permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to get full detailed information about
+ recently running tasks, with full fidelity to the real state.
+ @hide -->
+ <permission android:name="android.permission.GET_DETAILED_TASKS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to change the Z-order of tasks.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REORDER_TASKS"
+ android:label="@string/permlab_reorderTasks"
+ android:description="@string/permdesc_reorderTasks"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
+ <permission android:name="android.permission.REMOVE_TASKS"
+ android:protectionLevel="signature|recents|role" />
+
+ <!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead.
+ @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
+ <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove tasks -->
+ <permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"
+ android:protectionLevel="signature|recents" />
+
+ <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
+ <permission android:name="android.permission.ACTIVITY_EMBEDDING"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to embed any other apps in untrusted embedding mode without the need
+ for the embedded app to consent.
+ <p>For now, this permission is only granted to the Assistant application selected by
+ the user.
+ {@see https://developer.android.com/guide/topics/large-screens/activity-embedding#trust_model}
+ @SystemApi
+ @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission")
+ @hide
+ -->
+ <permission android:name="android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to start any activity, regardless of permission
+ protection or exported state.
+ @hide -->
+ <permission android:name="android.permission.START_ANY_ACTIVITY"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to start activities from background -->
+ <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"
+ android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role" />
+
+ <!-- Allows an application to start foreground services from the background at any time.
+ <em>This permission is not for use by third-party applications</em>,
+ with the only exception being if the app is the default SMS app.
+ Otherwise, it's only usable by privileged apps, app verifier app, and apps with
+ any of the EMERGENCY or SYSTEM GALLERY roles.
+ -->
+ <permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"
+ android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier|role"/>
+
+ <!-- Allows an application to request interactive options when sending a broadcast.
+ @hide -->
+ <permission android:name="android.permission.BROADCAST_OPTION_INTERACTIVE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Must be required by activities that handle the intent action
+ {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
+ hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS" />
+
+ <!-- Allows an application to start an activity as another app, provided that app has been
+ granted a permissionToken from the ActivityManagerService.
+ @hide -->
+ <permission android:name="android.permission.START_ACTIVITY_AS_CALLER"
+ android:protectionLevel="signature" />
+
+ <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
+ API is no longer supported. -->
+ <permission android:name="android.permission.RESTART_PACKAGES"
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to call
+ {@link android.app.ActivityManager#killBackgroundProcesses}.
+ <p>As of Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+ the {@link android.app.ActivityManager#killBackgroundProcesses} is no longer available to
+ third party applications. For backwards compatibility, the background processes of the
+ caller's own package will still be killed when calling this API. If the caller has
+ the system permission {@code KILL_ALL_BACKGROUND_PROCESSES}, other processes will be
+ killed too.
+
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi @hide Allows an application to call
+ {@link android.app.ActivityManager#killBackgroundProcesses}
+ to kill background processes of other apps.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.KILL_ALL_BACKGROUND_PROCESSES"
+ android:label="@string/permlab_killBackgroundProcesses"
+ android:description="@string/permdesc_killBackgroundProcesses"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to query process states and current
+ OOM adjustment scores.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows use of PendingIntent.getIntent(), .
+ @hide @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)
+ -->
+ <permission android:name="android.permission.GET_INTENT_SENDER_INTENT"
+ android:protectionLevel="signature" />
+
+ <!-- ================================== -->
+ <!-- Permissions affecting the display of other applications -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- Allows an app to create windows using the type
+ {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
+ shown on top of all other apps. Very few apps
+ should use this permission; these windows are intended for
+ system-level interaction with the user.
+
+ <p class="note"><strong>Note:</strong> If the app
+ targets API level 23 or higher, the app user must explicitly grant
+ this permission to the app through a permission management screen. The app requests
+ the user's approval by sending an intent with action
+ {@link android.provider.Settings#ACTION_MANAGE_OVERLAY_PERMISSION}.
+ The app can check whether it has this authorization by calling
+ {@link android.provider.Settings#canDrawOverlays
+ Settings.canDrawOverlays()}.
+ <p>Protection level: signature|setup|appop|installer|pre23|development -->
+ <permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
+ android:label="@string/permlab_systemAlertWindow"
+ android:description="@string/permdesc_systemAlertWindow"
+ android:protectionLevel="signature|setup|appop|installer|pre23|development" />
+
+ <!-- @SystemApi @hide Allows an application to create windows using the type
+ {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
+ shown on top of all other apps.
+
+ Allows an application to use
+ {@link android.view.WindowManager.LayoutsParams#setSystemApplicationOverlay(boolean)}
+ to create overlays that will stay visible, even if another window is requesting overlays to
+ be hidden through {@link android.view.Window#setHideOverlayWindows(boolean)}.
+
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY"
+ android:protectionLevel="signature|recents|role|installer"/>
+
+ <!-- @deprecated Use {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
+ @hide
+ -->
+ <permission android:name="android.permission.RUN_IN_BACKGROUND"
+ android:label="@string/permlab_runInBackground"
+ android:description="@string/permdesc_runInBackground"
+ android:protectionLevel="signature" />
+
+ <!-- @deprecated Use
+ {@link android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND}
+ @hide
+ -->
+ <permission android:name="android.permission.USE_DATA_IN_BACKGROUND"
+ android:label="@string/permlab_useDataInBackground"
+ android:description="@string/permdesc_useDataInBackground"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to set display offsets for the screen.
+ This permission is not available to third party applications. -->
+ <permission android:name="android.permission.SET_DISPLAY_OFFSET"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows a companion app to run in the background. This permission implies
+ {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND},
+ and allows to start a foreground service from the background.
+ If an app does not have to run in the background, but only needs to start a foreground
+ service from the background, consider using
+ {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND},
+ which is less powerful.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"
+ android:label="@string/permlab_runInBackground"
+ android:description="@string/permdesc_runInBackground"
+ android:protectionLevel="normal" />
+
+ <!-- Allows a companion app to start a foreground service from the background.
+ {@see android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND"
+ android:label="@string/permlab_startForegroundServicesFromBackground"
+ android:description="@string/permdesc_startForegroundServicesFromBackground"
+ android:protectionLevel="normal"/>
+
+ <!-- Allows a companion app to use data in the background.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND"
+ android:label="@string/permlab_useDataInBackground"
+ android:description="@string/permdesc_useDataInBackground"
+ android:protectionLevel="normal" />
+
+ <!-- Allows app to request to be associated with a device via
+ {@link android.companion.CompanionDeviceManager}
+ as a "watch"
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH"
+ android:label="@string/permlab_companionProfileWatch"
+ android:description="@string/permdesc_companionProfileWatch"
+ android:protectionLevel="normal" />
+
+ <!-- Allows app to request to be associated with a device via
+ {@link android.companion.CompanionDeviceManager}
+ as "glasses"
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_GLASSES"
+ android:protectionLevel="normal" />
+
+ <!-- Allows application to request to be associated with a virtual display capable of streaming
+ Android applications
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_APP_STREAMING})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows application to request to stream content from an Android host to a nearby device
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_NEARBY_DEVICE_STREAMING})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows application to request to be associated with a vehicle head unit capable of
+ automotive projection
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows application to request to be associated with a computer to share functionality
+ and/or data with other devices, such as notifications, photos and media
+ ({@link android.companion.AssociationRequest#DEVICE_PROFILE_COMPUTER})
+ by {@link android.companion.CompanionDeviceManager}.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_PROFILE_COMPUTER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to create a "self-managed" association.
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows a companion app to associate to Wi-Fi.
+ <p>Only for use by a single pre-approved app.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an app to read and listen to projection state.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.READ_PROJECTION_STATE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an app to set and release automotive projection.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an app to prevent non-system-overlay windows from being drawn on top of it -->
+ <permission android:name="android.permission.HIDE_OVERLAY_WINDOWS"
+ android:label="@string/permlab_hideOverlayWindows"
+ android:description="@string/permdesc_hideOverlayWindows"
+ android:protectionLevel="normal" />
+
+ <!-- ================================== -->
+ <!-- Permissions affecting the system wallpaper -->
+ <!-- ================================== -->
+ <eat-comment />
+
+ <!-- Allows applications to set the wallpaper.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.SET_WALLPAPER"
+ android:label="@string/permlab_setWallpaper"
+ android:description="@string/permdesc_setWallpaper"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to set the wallpaper hints.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.SET_WALLPAPER_HINTS"
+ android:label="@string/permlab_setWallpaperHints"
+ android:description="@string/permdesc_setWallpaperHints"
+ android:protectionLevel="normal" />
+
+ <!-- Allow the app to read the system and lock wallpaper images.
+ <p>Not for use by third-party applications.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.READ_WALLPAPER_INTERNAL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allow apps to always update wallpaper by sending data.
+ @SystemApi
+ @hide
+ @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission")
+ -->
+ <permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER"
+ android:protectionLevel="internal|role" />
+
+ <!-- ===================================================== -->
+ <!-- Permissions for changing the system clock / time zone -->
+ <!-- ===================================================== -->
+ <eat-comment />
+
+ <!-- Allows applications to set the system time directly.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_TIME"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows applications to set the system time zone directly.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.SET_TIME_ZONE"
+ android:label="@string/permlab_setTimeZone"
+ android:description="@string/permdesc_setTimeZone"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows telephony to suggest the time / time zone.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows applications like settings to suggest the user's manually chosen time / time zone.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows system clock time suggestions from an external clock / time source to be made.
+ The nature of "external" could be highly form-factor specific. Example, times
+ obtained via the VHAL for Android Auto OS.
+ <p>Not for use by third-party applications.
+ @SystemApi @hide
+ -->
+ <permission android:name="android.permission.SUGGEST_EXTERNAL_TIME"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows applications like settings to manage configuration associated with automatic time
+ and time zone detection.
+ <p>Not for use by third-party applications.
+ @SystemApi @hide
+ -->
+ <permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- ==================================================== -->
+ <!-- Permissions related to changing status bar -->
+ <!-- ==================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to expand or collapse the status bar.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.EXPAND_STATUS_BAR"
+ android:label="@string/permlab_expandStatusBar"
+ android:description="@string/permdesc_expandStatusBar"
+ android:protectionLevel="normal" />
+
+ <!-- ============================================================== -->
+ <!-- Permissions related to adding/removing shortcuts from Launcher -->
+ <!-- ============================================================== -->
+ <eat-comment />
+
+ <!-- Allows an application to install a shortcut in Launcher.
+ <p>In Android O (API level 26) and higher, the <code>INSTALL_SHORTCUT</code> broadcast no
+ longer has any effect on your app because it's a private, implicit
+ broadcast. Instead, you should create an app shortcut by using the
+ {@link android.content.pm.ShortcutManager#requestPinShortcut requestPinShortcut()}
+ method from the {@link android.content.pm.ShortcutManager} class.
+ <p>Protection level: normal
+ -->
+ <permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
+ android:label="@string/permlab_install_shortcut"
+ android:description="@string/permdesc_install_shortcut"
+ android:protectionLevel="normal"/>
+
+ <!-- <p class="caution"><strong>Don't use this permission in your app.</strong><br>This
+ permission is no longer supported.
+ -->
+ <permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
+ android:label="@string/permlab_uninstall_shortcut"
+ android:description="@string/permdesc_uninstall_shortcut"
+ android:protectionLevel="normal"/>
+
+ <!-- ==================================================== -->
+ <!-- Permissions related to accessing sync settings -->
+ <!-- ==================================================== -->
+ <eat-comment />
+
+ <!-- Allows applications to read the sync settings.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.READ_SYNC_SETTINGS"
+ android:description="@string/permdesc_readSyncSettings"
+ android:label="@string/permlab_readSyncSettings"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to write the sync settings.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.WRITE_SYNC_SETTINGS"
+ android:description="@string/permdesc_writeSyncSettings"
+ android:label="@string/permlab_writeSyncSettings"
+ android:protectionLevel="normal" />
+
+ <!-- Allows applications to read the sync stats.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.READ_SYNC_STATS"
+ android:description="@string/permdesc_readSyncStats"
+ android:label="@string/permlab_readSyncStats"
+ android:protectionLevel="normal" />
+
+ <!-- ============================================ -->
+ <!-- Permissions for low-level system interaction -->
+ <!-- ============================================ -->
+ <eat-comment />
+
+ <!-- @SystemApi @hide Change the screen compatibility mode of applications -->
+ <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to modify the current configuration, such
+ as locale.
+ <p>Protection level: signature|privileged|development -->
+ <permission android:name="android.permission.CHANGE_CONFIGURATION"
+ android:protectionLevel="signature|privileged|development|role" />
+
+ <!-- Allows an application to read or write the system settings.
+
+ <p class="note"><strong>Note:</strong> If the app targets API level 23
+ or higher, the app user
+ must explicitly grant this permission to the app through a permission management screen.
+ The app requests the user's approval by sending an intent with action
+ {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}. The app
+ can check whether it has this authorization by calling {@link
+ android.provider.Settings.System#canWrite Settings.System.canWrite()}.
+
+ <p>Protection level: signature|preinstalled|appop|pre23
+ -->
+ <permission android:name="android.permission.WRITE_SETTINGS"
+ android:label="@string/permlab_writeSettings"
+ android:description="@string/permdesc_writeSettings"
+ android:protectionLevel="signature|preinstalled|appop|pre23|role" />
+
+ <!-- Allows an application to modify the Google service map.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WRITE_GSERVICES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @TestApi @hide Allows an application to modify config settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WRITE_DEVICE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
+ <!-- @SystemApi @TestApi @hide Allows an application to modify only allowlisted settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
+ <!-- @SystemApi @TestApi @hide Allows an application to read/write sync disabled mode config.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG"
+ android:protectionLevel="signature|verifier|configurator"/>
+
+ <!-- @SystemApi @hide Allows an application to read config settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_DEVICE_CONFIG"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- @SystemApi @hide Allows applications like settings to read system-owned
+ application-specific locale configs.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @hide Allows applications to set an application-specific {@link LocaleConfig}.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_APP_SPECIFIC_LOCALECONFIG"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to monitor {@link android.provider.Settings.Config} access.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
+ android:protectionLevel="signature"/>
+
+ <!-- @SystemApi @TestApi Allows an application to call
+ {@link android.app.ActivityManager#forceStopPackage}.
+ @hide -->
+ <permission android:name="android.permission.FORCE_STOP_PACKAGES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to retrieve the content of the active window
+ An active window is the window that has fired an accessibility event. -->
+ <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Modify the global animation scaling factor.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_ANIMATION_SCALE"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @deprecated This functionality will be removed in the future; please do
+ not use. Allow an application to make its activities persistent. -->
+ <permission android:name="android.permission.PERSISTENT_ACTIVITY"
+ android:label="@string/permlab_persistentActivity"
+ android:description="@string/permdesc_persistentActivity"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to find out the space used by any package.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.GET_PACKAGE_SIZE"
+ android:label="@string/permlab_getPackageSize"
+ android:description="@string/permdesc_getPackageSize"
+ android:protectionLevel="normal" />
+
+ <!-- @deprecated No longer useful, see
+ {@link android.content.pm.PackageManager#addPackageToPreferred}
+ for details. -->
+ <permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"
+ android:protectionLevel="signature|installer|verifier" />
+
+ <!-- Allows an application to receive the
+ {@link android.content.Intent#ACTION_BOOT_COMPLETED} that is
+ broadcast after the system finishes booting. If you don't
+ request this permission, you will not receive the broadcast at
+ that time. Though holding this permission does not have any
+ security implications, it can have a negative impact on the
+ user experience by increasing the amount of time it takes the
+ system to start and allowing applications to have themselves
+ running without the user being aware of them. As such, you must
+ explicitly declare your use of this facility to make that visible
+ to the user.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"
+ android:label="@string/permlab_receiveBootCompleted"
+ android:description="@string/permdesc_receiveBootCompleted"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to broadcast sticky intents. These are
+ broadcasts whose data is held by the system after being finished,
+ so that clients can quickly retrieve that data without having
+ to wait for the next broadcast.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.BROADCAST_STICKY"
+ android:label="@string/permlab_broadcastSticky"
+ android:description="@string/permdesc_broadcastSticky"
+ android:protectionLevel="normal" />
+
+ <!-- Allows mounting and unmounting file systems for removable storage.
+ <p>Not for use by third-party applications.-->
+ <permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows formatting file systems for removable storage.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide -->
+ <permission android:name="android.permission.STORAGE_INTERNAL"
+ android:protectionLevel="signature" />
+
+ <!-- Allows access to ASEC non-destructive API calls
+ @hide -->
+ <permission android:name="android.permission.ASEC_ACCESS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows creation of ASEC volumes
+ @hide -->
+ <permission android:name="android.permission.ASEC_CREATE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows destruction of ASEC volumes
+ @hide -->
+ <permission android:name="android.permission.ASEC_DESTROY"
+ android:protectionLevel="signature" />
+
+ <!-- Allows mount / unmount of ASEC volumes
+ @hide -->
+ <permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
+ android:protectionLevel="signature" />
+
+ <!-- Allows rename of ASEC volumes
+ @hide -->
+ <permission android:name="android.permission.ASEC_RENAME"
+ android:protectionLevel="signature" />
+
+ <!-- Allows applications to write the apn settings and read sensitive fields of
+ an existing apn settings like user and password.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WRITE_APN_SETTINGS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows applications to change network connectivity state.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.CHANGE_NETWORK_STATE"
+ android:description="@string/permdesc_changeNetworkState"
+ android:label="@string/permlab_changeNetworkState"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to clear the caches of all installed
+ applications on the device.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.CLEAR_APP_CACHE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to use any media decoder when decoding for playback
+ @hide -->
+ <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to install and/or uninstall CA certificates on
+ behalf of the user.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CA_CERTIFICATES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to do certain operations needed for
+ interacting with the recovery (system update) system.
+ @hide -->
+ <permission android:name="android.permission.RECOVERY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to do certain operations needed for
+ resume on reboot feature.
+ @hide -->
+ <permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to read system update info.
+ @hide -->
+ <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows the system to bind to an application's task services
+ @hide -->
+ <permission android:name="android.permission.BIND_JOB_SERVICE"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_JOB_SERVICE"/>
+
+ <!-- Allows an application to initiate configuration updates
+ <p>An application requesting this permission is responsible for
+ verifying the source and integrity of any update before passing
+ it off to the various individual installer components
+ @hide -->
+ <permission android:name="android.permission.UPDATE_CONFIG"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to query the current time zone rules state
+ on device.
+ @SystemApi @hide
+ @deprecated Vestigial permission declaration. No longer used. -->
+ <permission android:name="android.permission.QUERY_TIME_ZONE_RULES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows a time zone rule updater application to request
+ the system installs / uninstalls timezone rules.
+ <p>An application requesting this permission is responsible for
+ verifying the source and integrity of the update before passing
+ it off to the installer components.
+ @SystemApi @hide
+ @deprecated Vestigial permission declaration. No longer used. -->
+ <permission android:name="android.permission.UPDATE_TIME_ZONE_RULES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows the system to reset throttling in shortcut manager.
+ @hide -->
+ <permission android:name="android.permission.RESET_SHORTCUT_MANAGER_THROTTLING"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the system to bind to the discovered Network Recommendation Service.
+ @SystemApi @hide -->
+ <permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"/>
+
+ <!-- Allows an application to enable, disable and change priority of
+ runtime resource overlays.
+ @hide -->
+ <permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to set, update and remove the credential management app.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CREDENTIAL_MANAGEMENT_APP"
+ android:protectionLevel="signature" />
+
+ <!-- Allows a font updater application to request that the system installs/uninstalls/updates
+ font files. @SystemApi @hide -->
+ <permission android:name="android.permission.UPDATE_FONTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to use the AttestationVerificationService.
+ @hide -->
+ <permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to export a AttestationVerificationService to verify attestations on
+ behalf of AttestationVerificationManager for system-defined attestation profiles.
+ @hide -->
+ <permission android:name="android.permission.VERIFY_ATTESTATION"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by any AttestationVerificationService to ensure that only the system can
+ bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_ATTESTATION_VERIFICATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the caller to generate keymint keys with the INCLUDE_UNIQUE_ID tag, which
+ uniquely identifies the device via the attestation certificate.
+ @hide @TestApi -->
+ <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the caller to bind with Remote Key Provisioning service.
+ @hide -->
+ <permission android:name="android.permission.BIND_RKP_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to get enabled credential manager providers.
+ @hide -->
+ <permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows a system application to be registered with credential manager without
+ having to be enabled by the user.
+ @hide @SystemApi -->
+ <permission android:name="android.permission.PROVIDE_DEFAULT_ENABLED_CREDENTIAL_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows specifying candidate credential providers to be queried in Credential Manager
+ get flows, or to be preferred as a default in the Credential Manager create flows.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS"
+ android:protectionLevel="normal" />
+
+ <!-- Allows a browser to invoke credential manager APIs on behalf of another RP.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN"
+ android:protectionLevel="normal" />
+
+ <!-- Allows a browser to invoke the set of query apis to get metadata about credential
+ candidates prepared during the CredentialManager.prepareGetCredential API.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS"
+ android:protectionLevel="normal" />
+
+ <!-- Allows permission to use Credential Manager UI for providing and saving credentials
+ @hide -->
+ <permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to be able to store and retrieve credentials from a remote
+ device.
+ <p>Protection level: signature|privileged|role -->
+ <permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- ========================================= -->
+ <!-- Permissions for special development tools -->
+ <!-- ========================================= -->
+ <eat-comment />
+
+ <!-- Allows an application to read or write the secure system settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+ android:protectionLevel="signature|privileged|development|role|installer" />
+
+ <!-- Allows an application to retrieve state dump information from system services.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.DUMP"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to start tracing for InputMethod and WindowManager.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_UI_TRACING"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to read the low-level system log files.
+ <p>Not for use by third-party applications, because
+ Log entries can contain the user's private information. -->
+ <permission android:name="android.permission.READ_LOGS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Configure an application for debugging.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_DEBUG_APP"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to access the data in Dropbox.
+ <p>Not for use by third-party applications.
+ @FlaggedApi("com.android.server.feature.flags.enable_read_dropbox_permission") -->
+ <permission android:name="android.permission.READ_DROPBOX_DATA"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to set the maximum number of (not needed)
+ application processes that can be running.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_PROCESS_LIMIT"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to control whether activities are immediately
+ finished when put in the background.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_ALWAYS_FINISH"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allow an application to request that a signal be sent to all persistent processes.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @hide @SystemApi Must be required by a
+ {@link com.android.service.tracing.TraceReportService}, to ensure that only the system
+ can bind to it.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.BIND_TRACE_REPORT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @SystemApi @TestApi
+ Allow an application to approve incident and bug reports to be
+ shared off-device. There can be only one application installed on the
+ device with this permission, and since this is a privileged permission, it
+ must be in priv-app.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.APPROVE_INCIDENT_REPORTS"
+ android:protectionLevel="signature|incidentReportApprover" />
+
+ <!-- @hide Allow an application to approve an incident or bug report approval from
+ the system. -->
+ <permission android:name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- ==================================== -->
+ <!-- Private permissions -->
+ <!-- ==================================== -->
+ <eat-comment />
+
+ <!-- Allows access to the list of accounts in the Accounts Service.
+ <p>Protection level: signature|privileged -->
+ <permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows but does not guarantee access to user passwords at the conclusion of add account
+ @hide -->
+ <permission android:name="android.permission.GET_PASSWORD"
+ android:protectionLevel="signature" />
+
+ <!-- Allows applications to RW to diagnostic resources.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.DIAGNOSTIC"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to open, close, or disable the status bar
+ and its icons.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.STATUS_BAR"
+ android:protectionLevel="signature|privileged|recents" />
+
+ <!-- Allows an application to trigger bugreport via shell using the bugreport API.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.TRIGGER_SHELL_BUGREPORT"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to trigger profcollect report upload via shell.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to be the status bar. Currently used only by SystemUI.apk
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.STATUS_BAR_SERVICE"
+ android:protectionLevel="signature|recents" />
+
+ <!-- Allows an application to bind to third party quick settings tiles.
+ <p>Should only be requested by the System, should be required by
+ TileService declarations.-->
+ <permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:protectionLevel="signature|recents" />
+
+ <!-- Allows SystemUI to request third party controls.
+ <p>Should only be requested by the System and required by
+ {@link android.service.controls.ControlsProviderService} declarations.
+ -->
+ <permission android:name="android.permission.BIND_CONTROLS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to force a BACK operation on whatever is the
+ top activity.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.FORCE_BACK"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to update device statistics.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.UPDATE_DEVICE_STATS"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi @hide Allows an application to collect application operation statistics.
+ Not for use by third party apps. -->
+ <permission android:name="android.permission.GET_APP_OPS_STATS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @SystemApi @hide Allows an application to collect historical application operation
+ statistics.
+ <p>Not for use by third party applications.
+ -->
+ <permission android:name="android.permission.GET_HISTORICAL_APP_OPS_STATS"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Allows an application to update application operation statistics. Not for
+ use by third party apps.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
+ android:protectionLevel="signature|privileged|installer|role" />
+
+ <!-- @SystemApi Allows an application to update the user app op restrictions.
+ Not for use by third party apps.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_APP_OPS_RESTRICTIONS"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @TestApi Allows an application to update the user app op modes.
+ Not for use by third party apps.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_APP_OPS_MODES"
+ android:protectionLevel="signature|installer|verifier|role" />
+
+ <!-- @SystemApi Allows an application to open windows that are for use by parts
+ of the system user interface.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
+ android:protectionLevel="signature|module|recents" />
+
+ <!-- Allows an application to avoid all toast rate limiting restrictions.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.UNLIMITED_TOASTS"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.UNLIMITED_TOASTS" />
+
+ <!-- @SystemApi Allows an application to use
+ {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
+ to hide non-system-overlay windows.
+ <p>Not for use by third-party applications.
+ @deprecated Use {@link android.Manifest.permission#HIDE_OVERLAY_WINDOWS} instead
+ @hide
+ -->
+ <permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- @SystemApi Allows an application to manage (create, destroy,
+ Z-order) application tokens in the window manager.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_APP_TOKENS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows System UI to register listeners for events from Window Manager.
+ @hide -->
+ <permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows the application to temporarily freeze the screen for a
+ full-screen transition. -->
+ <permission android:name="android.permission.FREEZE_SCREEN"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to inject user events (keys, touch, trackball)
+ into the event stream and deliver them to ANY window. Without this
+ permission, you can only deliver events to windows in your own process.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INJECT_EVENTS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to register an input filter which filters the stream
+ of user events (keys, touch, trackball) before they are dispatched to any window. -->
+ <permission android:name="android.permission.FILTER_EVENTS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to retrieve the window token from the accessibility manager. -->
+ <permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to modify accessibility information from another app. -->
+ <permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to perform accessibility operations (e.g. send events) on
+ behalf of another package. -->
+ <permission android:name="android.permission.ACT_AS_PACKAGE_FOR_ACCESSIBILITY"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to change the accessibility volume. -->
+ <permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
+ android:protectionLevel="signature" />
+
+ <!-- @FlaggedApi("com.android.server.accessibility.motion_event_observing")
+ @hide
+ @TestApi
+ Allows an accessibility service to observe motion events without consuming them. -->
+ <permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to collect frame statistics -->
+ <permission android:name="android.permission.FRAME_STATS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to temporary enable accessibility on the device. -->
+ <permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to launch detail settings activity of a particular
+ accessibility service.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @SystemApi Allows an application to watch and control how activities are
+ started globally in the system. Only for is in debugging
+ (usually the monkey command).
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SET_ACTIVITY_WATCHER"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to call the activity manager shutdown() API
+ to put the higher-level system there into a shutdown state.
+ @hide -->
+ <permission android:name="android.permission.SHUTDOWN"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to tell the activity manager to temporarily
+ stop application switches, putting it into a special mode that
+ prevents applications from immediately switching away from some
+ critical UI such as the home screen.
+ @hide -->
+ <permission android:name="android.permission.STOP_APP_SWITCHES"
+ android:protectionLevel="signature|privileged|recents" />
+
+ <!-- @SystemApi Allows an application to retrieve private information about
+ the current top activity, such as any assist context it can provide.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
+ android:protectionLevel="signature|recents" />
+
+ <!-- @SystemApi Allows an application to set the system audio caption and its UI
+ enabled state.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows an application to retrieve the current state of keys and
+ switches.
+ <p>Not for use by third-party applications.
+ @deprecated The API that used this permission has been removed. -->
+ <permission android:name="android.permission.READ_INPUT_STATE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_INPUT_METHOD"
+ android:protectionLevel="signature" />
+
+ <!-- Allows access to Test APIs defined in {@link android.view.inputmethod.InputMethodManager}.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.TEST_INPUT_METHOD"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.media.midi.MidiDeviceService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.printservice.PrintService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_PRINT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.printservice.recommendation.RecommendationService},
+ to ensure that only the system can bind to it.
+ @hide
+ @SystemApi
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows applications to get the installed and enabled print services.
+ @hide
+ @SystemApi
+ <p>Protection level: signature|preinstalled
+ -->
+ <permission android:name="android.permission.READ_PRINT_SERVICES"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- Allows applications to get the currently recommended print services for printers.
+ @hide
+ @SystemApi
+ <p>Protection level: signature|preinstalled
+ -->
+ <permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
+ or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
+ the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_NFC_SERVICE"
+ android:protectionLevel="signature|module" />
+
+ <!-- Must be required by a {@link android.service.quickaccesswallet.QuickAccessWalletService}
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by the CompanionDeviceManager to ensure that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by any
+ {@link android.companion.CompanionDeviceService}s
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_COMPANION_DEVICE_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Must be required by the RuntimePermissionPresenterService to ensure
+ that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a TextService (e.g. SpellCheckerService)
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_TEXT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Must be required by a AttentionService
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_ATTENTION_SERVICE"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_ATTENTION_SERVICE" />
+
+ <!-- @SystemApi Must be required by a RotationResolverService
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_ROTATION_RESOLVER_SERVICE"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_ROTATION_RESOLVER_SERVICE" />
+
+ <!-- Must be required by a {@link android.net.VpnService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_VPN_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_WALLPAPER"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- Must be required by a game service to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_GAME_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_VOICE_INTERACTION"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Must be required by a {@link android.service.voice.HotwordDetectionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_HOTWORD_DETECTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to manage hotword detection and visual query detection
+ on the device.
+ <p>Protection level: internal|preinstalled
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.MANAGE_HOTWORD_DETECTION"
+ android:protectionLevel="internal|preinstalled" />
+
+ <!-- @SystemApi Must be required by a {@link android.service.voice.VisualQueryDetectionService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_VISUAL_QUERY_DETECTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to subscribe to keyguard locked (i.e., showing) state.
+ <p>Protection level: signature|role
+ <p>Intended for use by ROLE_ASSISTANT and signature apps only.
+ -->
+ <permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE"
+ android:protectionLevel="signature|module|role"/>
+
+ <!-- Must be required by a {@link android.service.autofill.AutofillService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a
+ {@link android.service.assist.classification.FieldClassificationService},
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_FIELD_CLASSIFICATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a CredentialProviderService to ensure that only the
+ system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Alternative version of android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE.
+ This permission was renamed during the O previews but it was supported on the final O
+ release, so we need to carry it over.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_AUTOFILL"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.service.autofill.AutofillFieldClassificationService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.service.autofill.InlineSuggestionRenderService}
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.textclassifier.TextClassifierService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a
+ {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.selectiontoolbar.SelectionToolbarRenderService,
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.contentcapture.ContentCaptureService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.translation.TranslationService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_TRANSLATION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows apps to use ui translation functions.
+ <p>Protection level: signature|privileged
+ @hide Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.MANAGE_UI_TRANSLATION"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a
+ android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"
+ android:protectionLevel="signature" />
+
+
+ <!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_MUSIC_RECOGNITION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.service.autofill.augmented.AugmentedAutofillService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.service.voice.VoiceInteractionService} implementation
+ to enroll its own sound models. This is a more restrictive permission than the higher-level
+ permission KEYPHRASE_ENROLLMENT_APPLICATION. For the caller to enroll sound models with
+ this permission, it must hold the permission and be the active VoiceInteractionService in
+ the system.
+ {@see Settings.Secure.VOICE_INTERACTION_SERVICE}
+ @hide @SystemApi Intended for OEM and system apps.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a keyphrase enrollment application, to enroll sound models. This is
+ treated as a higher-level permission to MANAGE_VOICE_KEYPHRASES as a caller can enroll
+ sound models at any time. This permission should be reserved for system enrollment
+ applications detected by {@link android.hardware.soundtrigger.KeyphraseEnrollmentInfo}
+ only.
+ @hide @SystemApi Intended for OEM and system apps.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.KEYPHRASE_ENROLLMENT_APPLICATION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
+ to ensure that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_REMOTE_DISPLAY"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a android.media.tv.ad.TvAdService to ensure that only the system can
+ bind to it.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("android.media.tv.flags.enable_ad_service_fw")
+ -->
+ <permission android:name="android.permission.BIND_TV_AD_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.media.tv.TvInputService}
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_TV_INPUT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.media.tv.interactive.TvInteractiveAppService}
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_TV_INTERACTIVE_APP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi
+ Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications. </p>
+ @hide -->
+ <permission android:name="android.permission.BIND_TV_REMOTE_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi
+ Must be required for a virtual remote controller for TV.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications. </p>
+ @hide -->
+ <permission android:name="android.permission.TV_VIRTUAL_REMOTE_CONTROLLER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to change HDMI CEC active source.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to modify parental controls
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to read TvContentRatingSystemInfo
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to notify TV inputs by sending broadcasts.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications.
+ @hide @SystemApi -->
+ <permission android:name="android.permission.NOTIFY_TV_INPUTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- This permission is required among systems services when accessing
+ tuner resource management related APIs or information.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ <p>This should only be used by the OEM TvInputService.
+ @hide -->
+ <permission android:name="android.permission.TUNER_RESOURCE_ACCESS"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- This permission is required among systems services to always keep the
+ binding with TvInputManagerService.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ <p>This should only be used by the OEM TvInputService.
+ @hide -->
+ <permission android:name="android.permission.ALWAYS_BOUND_TV_INPUT"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- @SystemApi This permission is required by Media Resource Manager Service when
+ system services create MediaCodecs on behalf of other processes and apps.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" />
+
+ <!-- This permission is required by Media Resource Observer Service when
+ accessing its registerObserver Api.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by a {@link android.media.routing.MediaRouteService}
+ to ensure that only the system can interact with it.
+ @hide -->
+ <permission android:name="android.permission.BIND_ROUTE_PROVIDER"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by device administration receiver, to ensure that only the
+ system can interact with it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_DEVICE_ADMIN"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Required to add or remove another application as a device admin.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an app to reset the device password.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.RESET_PASSWORD"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an app to lock the device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.LOCK_DEVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows low-level access to setting the orientation (actually
+ rotation) of the screen.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SET_ORIENTATION"
+ android:protectionLevel="signature|recents" />
+
+ <!-- @SystemApi Allows low-level access to setting the pointer speed.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.SET_POINTER_SPEED"
+ android:protectionLevel="signature" />
+
+ <!-- Allows low-level access to setting input device calibration.
+ <p>Not for use by normal applications.
+ @hide -->
+ <permission android:name="android.permission.SET_INPUT_CALIBRATION"
+ android:protectionLevel="signature" />
+
+ <!-- Allows low-level access to setting the keyboard layout.
+ <p>Not for use by third-party applications.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an app to schedule a prioritized alarm that can be used to perform
+ background work even when the device is in doze.
+ <p>Not for use by third-party applications.
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.SCHEDULE_PRIORITIZED_ALARM"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- Allows applications to use exact alarm APIs.
+ <p>This is a special access permission that can be revoked by the system or the user.
+ It should only be used to enable <b>user-facing features</b> that require exact alarms.
+ For more details, please go through the associated
+ <a href="{@docRoot}training/scheduling/alarms#exact">developer docs</a>.
+ <p>Apps need to target API {@link android.os.Build.VERSION_CODES#S} or above to be able to
+ request this permission. Note that apps targeting lower API levels do not need this
+ permission to use exact alarm APIs.
+ <p>Apps that hold this permission and target API
+ {@link android.os.Build.VERSION_CODES#TIRAMISU} and below always stay in the
+ {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_WORKING_SET WORKING_SET} or
+ lower standby bucket.
+ <p>If your app relies on exact alarms for core functionality, it can instead request
+ {@link android.Manifest.permission#USE_EXACT_ALARM} once it targets API
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}. All apps using exact alarms for secondary
+ features (which should still be user facing) should continue using this permission.
+ <p>Protection level: signature|privileged|appop
+ -->
+ <permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
+ android:label="@string/permlab_schedule_exact_alarm"
+ android:description="@string/permdesc_schedule_exact_alarm"
+ android:protectionLevel="signature|privileged|appop"/>
+
+ <!-- Allows apps to use exact alarms just like with {@link
+ android.Manifest.permission#SCHEDULE_EXACT_ALARM} but without needing to request this
+ permission from the user.
+ <p><b> This is only intended for use by apps that rely on exact alarms for their core
+ functionality.</b> You should continue using {@code SCHEDULE_EXACT_ALARM} if your app needs
+ exact alarms for a secondary feature that users may or may not use within your app.
+ <p> Keep in mind that this is a powerful permission and app stores may enforce policies to
+ audit and review the use of this permission. Such audits may involve removal from the app
+ store if the app is found to be misusing this permission.
+ <p> Apps need to target API {@link android.os.Build.VERSION_CODES#TIRAMISU} or above to be
+ able to request this permission. Note that only one of {@code USE_EXACT_ALARM} or
+ {@code SCHEDULE_EXACT_ALARM} should be requested on a device. If your app is already using
+ {@code SCHEDULE_EXACT_ALARM} on older SDKs but needs {@code USE_EXACT_ALARM} on SDK 33 and
+ above, then {@code SCHEDULE_EXACT_ALARM} should be declared with a max-sdk attribute, like:
+ <pre>
+ &lt;uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"
+ &Tab; android:maxSdkVersion="32" /&gt;
+ </pre>
+ <p>Apps that hold this permission, always stay in the
+ {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_WORKING_SET WORKING_SET} or
+ lower standby bucket.
+ -->
+ <permission android:name="android.permission.USE_EXACT_ALARM"
+ android:label="@string/permlab_use_exact_alarm"
+ android:description="@string/permdesc_use_exact_alarm"
+ android:protectionLevel="normal"/>
+
+ <!-- Allows an application to query tablet mode state and monitor changes
+ in it.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.TABLET_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to request installing packages. Apps
+ targeting APIs greater than 25 must hold this permission in
+ order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
+ android:label="@string/permlab_requestInstallPackages"
+ android:description="@string/permdesc_requestInstallPackages"
+ android:protectionLevel="signature|appop" />
+
+ <!-- Allows an application to request deleting packages. Apps
+ targeting APIs {@link android.os.Build.VERSION_CODES#P} or greater must hold this
+ permission in order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE} or
+ {@link android.content.pm.PackageInstaller#uninstall}.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
+ android:label="@string/permlab_requestDeletePackages"
+ android:description="@string/permdesc_requestDeletePackages"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to install packages.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.INSTALL_PACKAGES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to install self updates. This is a limited version
+ of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_SELF_UPDATES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to install updates. This is a limited version
+ of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to install existing system packages. This is a limited
+ version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ <p>Not for use by third-party applications.
+ TODO(b/80204953): remove this permission once we have a long-term solution.
+ @hide
+ -->
+ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows an application to use the package installer v2 APIs.
+ <p>The package installer v2 APIs are still a work in progress and we're
+ currently validating they work in all scenarios.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="com.android.permission.USE_INSTALLER_V2"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @TestApi Allows a testOnly application to get installed.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_TEST_ONLY_PACKAGE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to install DPCs only, an application is
+ considered a DPC if it has a {@link android.app.admin.DeviceAdminReceiver}
+ protected by {@link android.Manifest.permission#BIND_DEVICE_ADMIN).
+ This is a limited version of
+ {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_DPC_PACKAGES"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to read resolved paths to the APKs (Base and any splits)
+ of a session based install.
+ <p>Not for use by third-party applications.
+ @hide
+ @FlaggedApi("android.content.pm.get_resolved_apk_path")
+ -->
+ <permission android:name="android.permission.READ_INSTALLED_SESSION_PATHS"
+ android:protectionLevel="signature|installer" />
+ <uses-permission android:name="android.permission.READ_INSTALLED_SESSION_PATHS" />
+
+ <!-- Allows an application to use System Data Loaders.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @TestApi Allows an application to clear user data.
+ <p>Not for use by third-party applications
+ @hide
+ -->
+ <permission android:name="android.permission.CLEAR_APP_USER_DATA"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @hide Allows an application to get the URI permissions
+ granted to another application.
+ <p>Not for use by third-party applications
+ -->
+ <permission android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to clear the URI permissions
+ granted to another application.
+ <p>Not for use by third-party applications
+ -->
+ <permission
+ android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide
+ Allows an application to change the status of Scoped Access Directory requests granted or
+ rejected by the user.
+ <p>This permission should <em>only</em> be requested by the platform
+ settings app. This permission cannot be granted to third-party apps.
+ <p>Protection level: signature
+ -->
+ <permission
+ android:name="android.permission.MANAGE_SCOPED_ACCESS_DIRECTORY_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide
+ Allows an application to change the status of a persistable URI permission granted
+ to another application.
+ <p>This permission should <em>only</em> be requested by the platform
+ settings app. This permission cannot be granted to third-party apps.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.FORCE_PERSISTABLE_URI_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- Old permission for deleting an app's cache files, no longer used,
+ but signals for us to quietly ignore calls instead of throwing an exception.
+ <p>Protection level: signature|privileged -->
+ <permission android:name="android.permission.DELETE_CACHE_FILES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to delete cache files.
+ @hide -->
+ <permission android:name="android.permission.INTERNAL_DELETE_CACHE_FILES"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to delete packages.
+ <p>Not for use by third-party applications.
+ <p>Starting in {@link android.os.Build.VERSION_CODES#N}, user confirmation is requested
+ when the application deleting the package is not the same application that installed the
+ package. -->
+ <permission android:name="android.permission.DELETE_PACKAGES"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to move location of installed package.
+ @hide -->
+ <permission android:name="android.permission.MOVE_PACKAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @TestApi Allows an application to keep uninstalled packages as apks.
+ @hide -->
+ <permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to change whether an application component (other than its own) is
+ enabled or not.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi @TestApi iAllows an application to grant specific permissions.
+ @hide -->
+ <permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature|installer|verifier" />
+
+ <!-- @SystemApi Allows an application to launch the settings page which manages various
+ permissions.
+ @hide -->
+ <permission android:name="android.permission.LAUNCH_PERMISSION_SETTINGS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an app that has this permission and the permissions to install packages
+ to request certain runtime permissions to be granted at installation.
+ @hide -->
+ <permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature|installer|verifier" />
+
+ <!-- @SystemApi Allows an application to revoke specific permissions.
+ @hide -->
+ <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature|installer|verifier" />
+
+ <!-- @TestApi Allows an application to revoke the POST_NOTIFICATIONS permission from an app
+ without killing the app. Only granted to the shell.
+ @hide -->
+ <permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows the system to read runtime permission state.
+ @hide -->
+ <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows the system to restore runtime permission state. This might grant
+ permissions, hence this is a more scoped, less powerful variant of GRANT_RUNTIME_PERMISSIONS.
+ Among other restrictions this cannot override user choices.
+ @hide -->
+ <permission android:name="android.permission.RESTORE_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @TestApi Allows an application to change policy_fixed permissions.
+ @hide -->
+ <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @SystemApi @TestApi Allows an application to upgrade runtime permissions.
+ @hide -->
+ <permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to allowlist restricted permissions
+ on any of the allowlists.
+ @hide -->
+ <permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @SystemApi Allows an application to an exempt an app from having its permission be
+ auto-revoked when unused for an extended period of time.
+ @hide -->
+ <permission android:name="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @hide Allows an application to observe permission changes. -->
+ <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to start and stop one time permission sessions
+ @hide -->
+ <permission android:name="android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @SystemApi Allows an application to manage the holders of a role.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
+ android:protectionLevel="signature|installer|module" />
+ <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
+
+ <!-- @SystemApi Allows an application to manage the holders of roles associated with default
+ applications.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_DEFAULT_APPLICATIONS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to bypass role qualification. This allows switching role
+ holders to otherwise non eligible holders. Only the shell is allowed to do this, the
+ qualification for the shell role itself cannot be bypassed, and each role needs to
+ explicitly allow bypassing qualification in its definition. The bypass state will not be
+ persisted across reboot.
+ @hide -->
+ <permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Allows an application to observe role holder changes.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
+ android:protectionLevel="signature|installer|module" />
+
+ <!-- Allows an application to manage the companion devices.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
+ android:protectionLevel="module|signature|role" />
+
+ <!-- Allows an application to subscribe to notifications about the presence status change
+ of their associated companion device
+ -->
+ <permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE"
+ android:label="@string/permlab_observeCompanionDevicePresence"
+ android:description="@string/permdesc_observeCompanionDevicePresence"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to subscribe to notifications about the nearby devices' presence
+ status change base on the UUIDs.
+ <p>Not for use by third-party applications.</p>
+ @FlaggedApi("android.companion.flags.device_presence")
+ -->
+ <permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to deliver companion messages to system
+ -->
+ <permission android:name="android.permission.DELIVER_COMPANION_MESSAGES"
+ android:label="@string/permlab_deliverCompanionMessages"
+ android:description="@string/permdesc_deliverCompanionMessages"
+ android:protectionLevel="normal" />
+
+ <!-- @hide @FlaggedApi("android.companion.flags.companion_transport_apis")
+ Allows an application to send and receive messages via CDM transports.
+ -->
+ <permission android:name="android.permission.USE_COMPANION_TRANSPORTS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to create new companion device associations.
+ @SystemApi
+ @hide -->
+ <permission android:name="android.permission.ASSOCIATE_COMPANION_DEVICES"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to rotate a surface by arbitrary degree.
+ This is a sub-feature of ACCESS_SURFACE_FLINGER and can be granted in a more concrete way.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.ROTATE_SURFACE_FLINGER"
+ android:protectionLevel="signature|recents" />
+
+ <!-- Allows an application to provide hints to SurfaceFlinger that can influence
+ its wakes up time to compose the next frame. This is a subset of the capabilities granted
+ by {@link #ACCESS_SURFACE_FLINGER}.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.WAKEUP_SURFACE_FLINGER"
+ android:protectionLevel="signature|recents" />
+
+ <!-- Allows an application to take screen shots and more generally
+ get access to the frame buffer data.
+ <p>Not for use by third-party applications.
+ @hide
+ @removed -->
+ <permission android:name="android.permission.READ_FRAME_BUFFER"
+ android:protectionLevel="signature|recents" />
+
+ <!-- Allows an application to change the touch mode state.
+ Without this permission, an app can only change the touch mode
+ if it currently has focus.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to use InputFlinger's low level features.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_INPUT_FLINGER"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to disable/enable input devices.
+ Could be used to prevent unwanted touch events
+ on a touchscreen, for example during swimming or rain.
+ @hide -->
+ <permission android:name="android.permission.DISABLE_INPUT_DEVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to configure and connect to Wifi displays -->
+ <permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
+
+ <!-- Allows an application to control low-level features of Wifi displays
+ such as opening an RTSP socket. This permission should only be used
+ by the display manager.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to control the color modes set for displays system-wide.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to control the lights on the device.
+ @hide
+ @SystemApi
+ @TestApi -->
+ <permission android:name="android.permission.CONTROL_DEVICE_LIGHTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to control the color saturation of the display.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to control display color transformations.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to collect usage information about brightness slider changes.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @SystemApi
+ @TestApi -->
+ <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to collect ambient light stats.
+ <p>Not for use by third party applications.</p>
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to modify the display brightness configuration
+ @hide
+ @SystemApi
+ @TestApi -->
+ <permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows an application to control the system's display brightness
+ @hide -->
+ <permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to override the display mode requests
+ so the app requested mode will be selected and user settings and display
+ policies will be ignored.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to modify the refresh rate switching type. This
+ matches Setting.Secure.MATCH_CONTENT_FRAME_RATE.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to modify the user preferred display mode.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to modify the HDR conversion mode.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.MODIFY_HDR_CONVERSION_MODE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to control VPN.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CONTROL_VPN"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.CONTROL_VPN" />
+
+ <!-- Allows an application to access and modify always-on VPN configuration.
+ <p>Not for use by third-party or privileged applications.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to capture the audio from tuner input devices types,
+ such as FM_TUNER.
+
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CAPTURE_TUNER_AUDIO_INPUT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to capture audio output.
+ Use the {@code CAPTURE_MEDIA_OUTPUT} permission if only the {@code USAGE_UNKNOWN}),
+ {@code USAGE_MEDIA}) or {@code USAGE_GAME}) usages are intended to be captured.
+ <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to capture the audio played by other apps
+ that have set an allow capture policy of
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}.
+
+ Without this permission, only audio with an allow capture policy of
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_ALL} can be used.
+
+ There are strong restriction listed at
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}
+ on what an app can do with the captured audio.
+
+ See {@code CAPTURE_AUDIO_OUTPUT} for capturing audio use cases other than media playback.
+
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CAPTURE_MEDIA_OUTPUT"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to capture the audio played by other apps
+ with the {@code USAGE_VOICE_COMMUNICATION} usage.
+
+ The application may opt out of capturing by setting an allow capture policy of
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
+
+ There are strong restriction listed at
+ {@link android.media.AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}
+ on what an app can do with the captured audio.
+
+ See {@code CAPTURE_AUDIO_OUTPUT} and {@code CAPTURE_MEDIA_OUTPUT} for capturing
+ audio use cases other than voice communication playback.
+
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to capture audio for hotword detection.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to access the ultrasound content.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.ACCESS_ULTRASOUND"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Puts an application in the chain of trust for sound trigger
+ operations. Being in the chain of trust allows an application to
+ delegate an identity of a separate entity to the sound trigger system
+ and vouch for the authenticity of this identity.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.SOUNDTRIGGER_DELEGATE_IDENTITY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to modify audio routing and override policy decisions.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!--@SystemApi Allows an application to modify system audio settings that shouldn't be
+ controllable by external apps, such as volume settings or volume behaviors for audio
+ devices, regardless of their connection status.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to access the uplink and downlink audio of an ongoing
+ call.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.CALL_AUDIO_INTERCEPTION"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @TestApi Allows an application to query audio related state.
+ @hide -->
+ <permission android:name="android.permission.QUERY_AUDIO_STATE"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows an application to modify what effects are applied to all audio
+ (matching certain criteria) from any application.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to disable system sound effects when the user exits one of
+ the application's activities.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.DISABLE_SYSTEM_SOUND_EFFECTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to provide remote displays.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.REMOTE_DISPLAY_PROVIDER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to capture video output.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @removed -->
+ <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to capture secure video output.
+ <p>Not for use by third-party applications.</p>
+ @hide
+ @removed -->
+ <permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows an application to know what content is playing and control its playback.
+ <p>Not for use by third-party applications due to privacy of media consumption</p> -->
+ <permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to control the routing of media apps.
+ <p>Only for use by role COMPANION_DEVICE_WATCH</p>
+ @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control")
+ -->
+ <permission android:name="android.permission.MEDIA_ROUTING_CONTROL"
+ android:protectionLevel="signature|appop" />
+
+ <!-- @SystemApi @hide Allows an application to set the volume key long-press listener.
+ <p>When it's set, the application will receive the volume key long-press event
+ instead of changing volume.</p>
+ <p>Not for use by third-party applications</p> -->
+ <permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @SystemApi @hide Allows an application to set media key event listener.
+ <p>When it's set, the application will receive the media key event before
+ any other media sessions. If the event is handled by the listener, other sessions
+ cannot get the event.</p>
+ <p>Not for use by third-party applications</p> -->
+ <permission android:name="android.permission.SET_MEDIA_KEY_LISTENER"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @SystemApi Required to be able to disable the device (very dangerous!).
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.BRICK"
+ android:protectionLevel="signature" />
+
+ <!-- Required to be able to reboot the device.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.REBOOT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows low-level access to power management.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.DEVICE_POWER"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows toggling battery saver on the system.
+ Superseded by DEVICE_POWER permission. @hide @SystemApi
+ -->
+ <permission android:name="android.permission.POWER_SAVER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows providing the system with battery predictions.
+ Superseded by DEVICE_POWER permission. @hide @SystemApi
+ -->
+ <permission android:name="android.permission.BATTERY_PREDICTION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows access to the PowerManager.userActivity function.
+ <p>Not for use by third-party applications. @hide @SystemApi -->
+ <permission android:name="android.permission.USER_ACTIVITY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide @SystemApi Allows an application to manage Low Power Standby settings.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_LOW_POWER_STANDBY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide @SystemApi Allows an application to request ports to remain open during
+ Low Power Standby.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SET_LOW_POWER_STANDBY_PORTS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows low-level access to tun tap driver -->
+ <permission android:name="android.permission.NET_TUNNELING"
+ android:protectionLevel="signature|role" />
+
+ <!-- Run as a manufacturer test application, running as the root user.
+ Only available when the device is running in manufacturer test mode.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.FACTORY_TEST"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @TestApi @SystemApi Allows an application to broadcast the intent {@link
+ android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS}.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"
+ android:protectionLevel="signature|privileged|recents" />
+ <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
+
+ <!-- Allows an application to broadcast a notification that an application
+ package has been removed.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.BROADCAST_PACKAGE_REMOVED"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to broadcast an SMS receipt notification.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.BROADCAST_SMS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to broadcast a WAP PUSH receipt notification.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.BROADCAST_WAP_PUSH"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to broadcast privileged networking requests.
+ <p>Not for use by third-party applications.
+ @hide
+ @deprecated Use {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} instead
+ -->
+ <permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Not for use by third-party applications. -->
+ <permission android:name="android.permission.MASTER_CLEAR"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Allows an application to call any phone number, including emergency
+ numbers, without going through the Dialer user interface for the user
+ to confirm the call being placed.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CALL_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
+
+ <!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
+ <permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to perform SIM Activation @hide -->
+ <permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows enabling/disabling location update notifications from
+ the radio.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows read/write access to the "properties" table in the checkin
+ database, to change values that get uploaded.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to collect component usage
+ statistics
+ <p>Declaring the permission implies intention to use the API and the user of the
+ device can grant permission through the Settings application.
+ <p>Protection level: signature|privileged|development|appop|retailDemo -->
+ <permission android:name="android.permission.PACKAGE_USAGE_STATS"
+ android:protectionLevel="signature|privileged|development|appop|retailDemo" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
+ <!-- @SystemApi @hide
+ @FlaggedApi("android.app.usage.report_usage_stats_permission")
+ Allows trusted system components to report events to UsageStatsManager -->
+ <permission android:name="android.permission.REPORT_USAGE_STATS"
+ android:protectionLevel="signature|module" />
+
+ <!-- Allows an application to query broadcast response stats (see
+ {@link android.app.usage.BroadcastResponseStats}).
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_BROADCAST_RESPONSE_STATS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- Allows a data loader to read a package's access logs. The access logs contain the
+ set of pages referenced over time.
+ <p>Declaring the permission implies intention to use the API and the user of the
+ device can grant permission through the Settings application.
+ <p>Protection level: signature|privileged|appop
+ <p>A data loader has to be the one which provides data to install an app.
+ <p>A data loader has to have both permission:LOADER_USAGE_STATS AND
+ appop:LOADER_USAGE_STATS allowed to be able to access the read logs. -->
+ <permission android:name="android.permission.LOADER_USAGE_STATS"
+ android:protectionLevel="signature|privileged|appop" />
+ <uses-permission android:name="android.permission.LOADER_USAGE_STATS" />
+
+ <!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
+ for callbacks when apps reach a certain usage time limit, etc. -->
+ <permission android:name="android.permission.OBSERVE_APP_USAGE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @hide @TestApi @SystemApi Allows an application to change the app idle state of an app.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide @SystemApi Allows an application to change the estimated launch time of an app.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide @SystemApi Allows an application to temporarily allowlist an inactive app to
+ access the network and acquire wakelocks.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Permission an application must hold in order to use
+ {@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
+ android:label="@string/permlab_requestIgnoreBatteryOptimizations"
+ android:description="@string/permdesc_requestIgnoreBatteryOptimizations"
+ android:protectionLevel="normal" />
+
+ <!-- Allows an application to collect battery statistics
+ <p>Protection level: signature|privileged|development -->
+ <permission android:name="android.permission.BATTERY_STATS"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!--Allows an application to manage statscompanion.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.STATSCOMPANION"
+ android:protectionLevel="signature" />
+
+ <!--@SystemApi @hide Allows an application to register stats pull atom callbacks.
+ <p>Not for use by third-party applications.-->
+ <permission android:name="android.permission.REGISTER_STATS_PULL_ATOM"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to read restricted stats from statsd.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_RESTRICTED_STATS"
+ android:protectionLevel="internal|privileged" />
+
+ <!-- @SystemApi Allows an application to control the backup and restore process.
+ <p>Not for use by third-party applications.
+ @hide pending API council -->
+ <permission android:name="android.permission.BACKUP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to make modifications to device settings such that these
+ modifications will be overridden by settings restore..
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE"
+ android:protectionLevel="signature|setup" />
+
+ <!-- @SystemApi Allows application to manage
+ {@link android.security.keystore.recovery.RecoveryController}.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.RECOVER_KEYSTORE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows a package to launch the secure full-backup confirmation UI.
+ ONLY the system process may hold this permission.
+ @hide -->
+ <permission android:name="android.permission.CONFIRM_FULL_BACKUP"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link android.widget.RemoteViewsService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature|privileged -->
+ <permission android:name="android.permission.BIND_REMOTEVIEWS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to tell the AppWidget service which application
+ can access AppWidget's data. The normal user flow is that a user
+ picks an AppWidget to go into a particular host, thereby giving that
+ host application access to the private data from the AppWidget app.
+ An application that has this permission should honor that contract.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.BIND_APPWIDGET"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows sysui to manage user grants of slice permissions. -->
+ <permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
+ keyguard widget
+ @hide -->
+ <permission android:name="android.permission.BIND_KEYGUARD_APPWIDGET"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Internal permission allowing an application to query/set which
+ applications can bind AppWidgets.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows applications to change the background data setting.
+ <p>Not for use by third-party applications.
+ @hide pending API council -->
+ <permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"
+ android:protectionLevel="signature" />
+
+ <!-- This permission can be used on content providers to allow the global
+ search system to access their data. Typically it used when the
+ provider has some permissions protecting it (which global search
+ would not be expected to hold), and added as a read-only permission
+ to the path in the provider where global search queries are
+ performed. This permission can not be held by regular applications;
+ it is used by applications to protect themselves from everyone else
+ besides global search.
+ <p>Protection level: signature|privileged -->
+ <permission android:name="android.permission.GLOBAL_SEARCH"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Internal permission protecting access to the global search
+ system: ensures that only the system can access the provider
+ to perform queries (since this otherwise provides unrestricted
+ access to a variety of content providers), and to write the
+ search statistics (to keep applications from gaming the source
+ ranking).
+ @hide -->
+ <permission android:name="android.permission.GLOBAL_SEARCH_CONTROL"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Internal permission to allows an application to read indexable data.
+ @hide -->
+ <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Internal permission to allows an application to bind to suggestion service.
+ @hide -->
+ <permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Internal permission to allows an application to access card content provider. -->
+ <permission android:name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- An application needs this permission for
+ {@link android.provider.Settings#ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY} to show its
+ {@link android.app.Activity} embedded in Settings app. -->
+ <permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- @SystemApi {@link android.app.Activity} should require this permission to ensure that only
+ the settings app can embed it in a multi pane window.
+ @hide -->
+ <permission android:name="android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows applications to set a live wallpaper.
+ @hide XXX Change to signature once the picker is moved to its
+ own apk as Ghod Intended. -->
+ <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows applications to set the wallpaper dim amount.
+ @hide. -->
+ <permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows applications to read dream settings and dream state.
+ @hide -->
+ <permission android:name="android.permission.READ_DREAM_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows applications to write dream settings, and start or stop dreaming.
+ @hide -->
+ <permission android:name="android.permission.WRITE_DREAM_STATE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows applications to read whether ambient display is suppressed. -->
+ <permission android:name="android.permission.READ_DREAM_SUPPRESSION"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allow an application to read and write the cache partition.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by default container service so that only
+ the system can bind to it and use it to copy
+ protected data to secure containers or files
+ accessible to the system.
+ @hide -->
+ <permission android:name="android.permission.COPY_PROTECTED_DATA"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Internal permission protecting access to the encryption methods
+ @hide
+ -->
+ <permission android:name="android.permission.CRYPT_KEEPER"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to read historical network usage for
+ specific networks and applications. @hide -->
+ <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to manage network policies (such as warning and disable
+ limits) and to define application-specific rules. @hide -->
+ <permission android:name="android.permission.MANAGE_NETWORK_POLICY"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide @deprecated use UPDATE_DEVICE_STATS instead -->
+ <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi @hide Allows an application to manage carrier subscription plans. -->
+ <permission android:name="android.permission.MANAGE_SUBSCRIPTION_PLANS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- C2DM permission.
+ @hide Used internally.
+ -->
+ <permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"
+ android:protectionLevel="signature" />
+ <uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
+
+ <!-- @SystemApi @hide Package verifier needs to have this permission before the PackageManager will
+ trust it to verify packages.
+ -->
+ <permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by package verifier receiver, to ensure that only the
+ system can interact with it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Rollback manager needs to have this permission before the PackageManager will
+ trust it to enable rollback.
+ -->
+ <permission android:name="android.permission.PACKAGE_ROLLBACK_AGENT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @TestApi @hide Allows managing apk level rollbacks. -->
+ <permission android:name="android.permission.MANAGE_ROLLBACKS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @TestApi @hide Allows testing apk level rollbacks. -->
+ <permission android:name="android.permission.TEST_MANAGE_ROLLBACKS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
+ <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
+ android:protectionLevel="signature|verifier" />
+
+ <!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
+ PackageManager will trust it to verify intent filters.
+ -->
+ <permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by intent filter verifier rintent-filtereceiver, to ensure that only the
+ system can interact with it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Domain verification agent package needs to have this permission before the
+ system will trust it to verify domains.
+
+ TODO(159952358): STOPSHIP: This must be updated to the new "internal" protectionLevel
+ -->
+ <permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT"
+ android:protectionLevel="internal|privileged" />
+
+ <!-- @SystemApi @hide Must be required by the domain verification agent's intent
+ BroadcastReceiver, to ensure that only the system can interact with it.
+ -->
+ <permission android:name="android.permission.BIND_DOMAIN_VERIFICATION_AGENT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @hide Allows an app like Settings to update the user's grants to what domains
+ an app is allowed to automatically open.
+ -->
+ <permission android:name="android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows applications to access serial ports via the SerialManager.
+ @hide -->
+ <permission android:name="android.permission.SERIAL_PORT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows the holder to access content providers from outside an ApplicationThread.
+ This permission is enforced by the ActivityManagerService on the corresponding APIs,
+ in particular ActivityManagerService#getContentProviderExternal(String) and
+ ActivityManagerService#removeContentProviderExternal(String).
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to hold an UpdateLock, recommending that a headless
+ OTA reboot *not* occur while the lock is held.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_LOCK"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application the opportunity to become a
+ {@link android.service.notification.NotificationAssistantService}.
+ User permission is still required before access is granted.
+ @hide -->
+ <permission android:name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi @TestApi Allows an application to read the current set of notifications, including
+ any metadata and intents attached.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
+ android:protectionLevel="signature|privileged|appop" />
+
+ <!-- Marker permission for applications that wish to access notification policy. This permission
+ is not supported on managed profiles.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
+ android:description="@string/permdesc_access_notification_policy"
+ android:label="@string/permlab_access_notification_policy"
+ android:protectionLevel="normal" />
+
+ <!-- Allows modification of do not disturb rules and policies. Only allowed for system
+ processes.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_NOTIFICATIONS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi @TestApi Allows adding/removing enabled notification listener components.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS"
+ android:protectionLevel="signature|installer" />
+ <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
+
+ <!-- @SystemApi Allows notifications to be colorized
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS"
+ android:protectionLevel="signature|setup|role" />
+
+ <!-- Allows access to keyguard secure storage. Only allowed for system processes.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
+ android:protectionLevel="signature|setup" />
+
+ <!-- Allows applications to set the initial lockscreen state.
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.SET_INITIAL_LOCK"
+ android:protectionLevel="signature|setup"/>
+
+ <!-- @TestApi Allows applications to set and verify lockscreen credentials.
+ @hide -->
+ <permission android:name="android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"
+ android:protectionLevel="signature"/>
+
+ <!-- @SystemApi Allows application to verify lockscreen credentials provided by a remote device.
+ @hide -->
+ <permission android:name="android.permission.CHECK_REMOTE_LOCKSCREEN"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_FINGERPRINT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows managing (adding, removing) face templates. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_FACE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
+ <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
+ android:protectionLevel="signature" />
+
+ <!-- Allows access to TestApis for various components in the biometric stack, including
+ FingerprintService, FaceService, BiometricService. Used by com.android.server.biometrics
+ CTS tests. @hide @TestApi -->
+ <permission android:name="android.permission.TEST_BIOMETRIC"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_BIOMETRIC"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <Biometric>Service authentication methods. Reserved for the system. @hide -->
+ <permission android:name="android.permission.USE_BIOMETRIC_INTERNAL"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the system to control the BiometricDialog (SystemUI). Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to set the advanced features on BiometricDialog (SystemUI), including
+ logo, logo description.
+ <p>Not for use by third-party applications.
+ @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
+ -->
+ <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to control keyguard. Only allowed for system processes.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_KEYGUARD"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to control keyguard features like secure notifications.
+ @hide -->
+ <permission android:name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to manage weak escrow token on the device. This permission
+ is not available to third party applications.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_WEAK_ESCROW_TOKEN"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to listen to trust changes. Only allowed for system processes.
+ @hide -->
+ <permission android:name="android.permission.TRUST_LISTENER"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to provide a trust agent.
+ @hide For security reasons, this is a platform-only permission. -->
+ <permission android:name="android.permission.PROVIDE_TRUST_AGENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to show a message
+ on the keyguard when asking to dismiss it.
+ @hide For security reasons, this is a platform-only permission. -->
+ <permission android:name="android.permission.SHOW_KEYGUARD_MESSAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to launch the trust agent settings activity.
+ @hide -->
+ <permission android:name="android.permission.LAUNCH_TRUST_AGENT_SETTINGS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Must be required by an {@link
+ android.service.trust.TrustAgentService},
+ to ensure that only the system can bind to it.
+ @hide -->
+ <permission android:name="android.permission.BIND_TRUST_AGENT"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link
+ android.service.notification.NotificationListenerService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Must be required by an {@link
+ android.service.notification.NotificationAssistantService} to ensure that only the system
+ can bind to it.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link
+ android.service.chooser.ChooserTargetService}, to ensure that
+ only the system can bind to it.
+ <p>Protection level: signature
+
+ @deprecated For publishing direct share targets, please follow the instructions in
+ https://developer.android.com/training/sharing/receive.html#providing-direct-share-targets
+ instead.
+ -->
+ <permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Must be held by services that extend
+ {@link android.service.resolver.ResolverRankerService}.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Must be required by services that extend
+ {@link android.service.resolver.ResolverRankerService}, to ensure that only the system can
+ bind to them.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_RESOLVER_RANKER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by a {@link
+ android.service.notification.ConditionProviderService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.service.dreams.DreamService},
+ to ensure that only the system can bind to it.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_DREAM_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.app.usage.CacheQuotaService} to ensure that only the
+ system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_CACHE_QUOTA_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to call into a carrier setup flow. It is up to the
+ carrier setup application to enforce that this permission is required
+ @hide This is not a third-party API (intended for OEMs and system apps). -->
+ <permission android:name="android.permission.INVOKE_CARRIER_SETUP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to listen for network condition observations.
+ @hide This is not a third-party API (intended for system apps). -->
+ <permission android:name="android.permission.ACCESS_NETWORK_CONDITIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to provision and access DRM certificates
+ @hide This is not a third-party API (intended for system apps). -->
+ <permission android:name="android.permission.ACCESS_DRM_CERTIFICATES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Api Allows an application to manage media projection sessions.
+ @hide This is not a third-party API (intended for system apps). -->
+ <permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @TestApi Allows an application to record sensitive content during media
+ projection. This is intended for on device screen recording system app.
+ @FlaggedApi("android.permission.flags.sensitive_notification_app_protection") -->
+ <permission android:name="android.permission.RECORD_SENSITIVE_CONTENT"
+ android:protectionLevel="signature"
+ android:featureFlag="android.permission.flags.sensitive_notification_app_protection"/>
+
+ <!-- @SystemApi Allows an application to read install sessions
+ @hide This is not a third-party API (intended for system apps). -->
+ <permission android:name="android.permission.READ_INSTALL_SESSIONS"
+ android:label="@string/permlab_readInstallSessions"
+ android:description="@string/permdesc_readInstallSessions"
+ android:protectionLevel="normal"/>
+
+ <!-- @SystemApi Allows an application to remove DRM certificates
+ @hide This is not a third-party API (intended for system apps). -->
+ <permission android:name="android.permission.REMOVE_DRM_CERTIFICATES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @deprecated Use {@link android.Manifest.permission#BIND_CARRIER_SERVICES} instead -->
+ <permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to interact with the currently active
+ {@link android.service.voice.VoiceInteractionService}.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- The system process that is allowed to bind to services in carrier apps will
+ have this permission. Carrier apps should use this permission to protect
+ their services that only the system is allowed to bind to.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.BIND_CARRIER_SERVICES"
+ android:label="@string/permlab_bindCarrierServices"
+ android:description="@string/permdesc_bindCarrierServices"
+ android:protectionLevel="signature|privileged" />
+
+ <!--
+ Allows the holder to start the permission usage screen for an app.
+ <p>Protection level: signature|installer
+ -->
+ <permission android:name="android.permission.START_VIEW_PERMISSION_USAGE"
+ android:label="@string/permlab_startViewPermissionUsage"
+ android:description="@string/permdesc_startViewPermissionUsage"
+ android:protectionLevel="signature|installer|module" />
+
+ <!--
+ @SystemApi
+ Allows the holder to start the screen to review permission decisions.
+ <p>Protection level: signature|installer
+ @hide -->
+ <permission android:name="android.permission.START_REVIEW_PERMISSION_DECISIONS"
+ android:label="@string/permlab_startReviewPermissionDecisions"
+ android:description="@string/permdesc_startReviewPermissionDecisions"
+ android:protectionLevel="signature|installer" />
+
+ <!--
+ Allows the holder to start the screen with a list of app features.
+ <p>Protection level: signature|installer
+ -->
+ <permission android:name="android.permission.START_VIEW_APP_FEATURES"
+ android:label="@string/permlab_startViewAppFeatures"
+ android:description="@string/permdesc_startViewAppFeatures"
+ android:protectionLevel="signature|installer" />
+
+ <!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
+ flag is set.
+ @hide -->
+ <permission android:name="android.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows applications to kill UIDs.
+ <p>This permission can be granted to the SYSTEM_SUPERVISOR role used for parental
+ controls.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.KILL_UID"
+ android:protectionLevel="signature|installer|role" />
+
+ <!-- @SystemApi Allows applications to read the local WiFi and Bluetooth MAC address.
+ @hide -->
+ <permission android:name="android.permission.LOCAL_MAC_ADDRESS"
+ android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"/>
+
+ <!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
+ @hide -->
+ <permission android:name="android.permission.PEERS_MAC_ADDRESS"
+ android:protectionLevel="signature|setup|role" />
+
+ <!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
+ can use this permission to ensure incoming Nfc messages are from the Nfc stack
+ and not simulated by another application.
+ @hide -->
+ <permission android:name="android.permission.DISPATCH_NFC_MESSAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows changing day / night mode when system is configured with
+ config_lockDayNightMode set to true. If requesting app does not have permission,
+ it will be ignored.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows entering or exiting car mode using a specified priority.
+ This permission is required to use UiModeManager while specifying a priority for the calling
+ app. A device manufacturer uses this permission to prioritize the apps which can
+ potentially request to enter car-mode on a device to help establish the correct behavior
+ where multiple such apps are active at the same time.
+ @hide -->
+ <permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Required to receive ACTION_ENTER_CAR_MODE_PRIVILEGED or
+ ACTION_EXIT_CAR_MODE_PRIVILEGED.
+ @hide -->
+ <permission android:name="android.permission.HANDLE_CAR_MODE_CHANGES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows the holder to send category_car notifications.
+ @hide -->
+ <permission
+ android:name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- The system process is explicitly the only one allowed to launch the
+ confirmation UI for full backup/restore -->
+ <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
+
+ <!-- @SystemApi Allows the holder to access and manage instant applications on the device.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_INSTANT_APPS"
+ android:protectionLevel="signature|installer|verifier|role" />
+ <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
+
+ <!-- Allows the holder to view the instant applications on the device.
+ @hide -->
+ <permission android:name="android.permission.VIEW_INSTANT_APPS"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- Allows the holder to manage whether the system can bind to services
+ provided by instant apps. This permission is intended to protect
+ test/development fucntionality and should be used only in such cases.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Allows receiving the usage of media resource e.g. video/audio codec and
+ graphic memory.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by system/priv apps when accessing the sound trigger
+ APIs given by {@link SoundTriggerManager}.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.MANAGE_SOUND_TRIGGER"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- Must be required by system/priv apps to run sound trigger recognition sessions while in
+ battery saver mode.
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.SOUND_TRIGGER_RUN_IN_BATTERY_SAVER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Must be required by system/priv apps implementing sound trigger detection services
+ @hide
+ @SystemApi -->
+ <permission android:name="android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows trusted applications to dispatch managed provisioning message to Managed
+ Provisioning app. If requesting app does not have permission, it will be ignored.
+ @hide -->
+ <permission android:name="android.permission.DISPATCH_PROVISIONING_MESSAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows the holder to read blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @SystemApi
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
+ @hide -->
+ <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows the holder to write blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @SystemApi
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
+ @hide -->
+ <permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.service.vr.VrListenerService}, to ensure that only
+ the system can bind to it.
+ <p>Protection level: signature -->
+ <permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by system apps when accessing restricted VR APIs.
+ @hide
+ @SystemApi
+ <p>Protection level: signature -->
+ <permission android:name="android.permission.RESTRICTED_VR_ACCESS"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- Required to make calls to {@link android.service.vr.IVrManager}.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_VR_MANAGER"
+ android:protectionLevel="signature" />
+
+ <!-- Required to access VR-Mode state and state change events via {android.app.VrStateCallback}
+ @hide -->
+ <permission android:name="android.permission.ACCESS_VR_STATE"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- Allows an application to allowlist tasks during lock task mode
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
+ android:protectionLevel="signature|setup" />
+
+ <!-- @SystemApi Allows an application to replace the app name displayed alongside notifications
+ in the N-release and later.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to show notifications before the device is provisioned.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.NOTIFICATION_DURING_SETUP"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to manage auto-fill sessions.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_AUTO_FILL"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to manage the content capture service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_CONTENT_CAPTURE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to manager the rotation resolver service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_ROTATION_RESOLVER"
+ android:protectionLevel="signature"/>
+
+ <!-- @SystemApi Allows an application to manage the cloudsearch service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_CLOUDSEARCH"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to manage the music recognition service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION"
+ android:protectionLevel="signature|privileged|role" />
+
+ <!-- @SystemApi Allows an application to manage speech recognition service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SPEECH_RECOGNITION"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to interact with the content suggestions service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to manage the app predictions service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_APP_PREDICTIONS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to manage the search ui service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SEARCH_UI"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to manage the smartspace service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_SMARTSPACE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to access the smartspace service as a client.
+ @FlaggedApi(android.app.smartspace.flags.Flags.FLAG_ACCESS_SMARTSPACE)
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.ACCESS_SMARTSPACE"
+ android:protectionLevel="signature|privileged|development" />
+
+ <!-- @SystemApi Allows an application to access the contextual search
+ service as a client.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.ACCESS_CONTEXTUAL_SEARCH"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.app.contextualsearch.flags.enable_service"/>
+
+ <!-- @SystemApi Allows an application to manage the wallpaper effects
+ generation service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an app to set the theme overlay in /vendor/overlay
+ being used.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MODIFY_THEME_OVERLAY"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an instant app to create foreground services.
+ <p>Protection level: signature|development|instant|appop -->
+ <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
+ android:protectionLevel="signature|development|instant|appop" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground}.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE"
+ android:description="@string/permdesc_foregroundService"
+ android:label="@string/permlab_foregroundService"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "camera".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"
+ android:description="@string/permdesc_foregroundServiceCamera"
+ android:label="@string/permlab_foregroundServiceCamera"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "connectedDevice".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"
+ android:description="@string/permdesc_foregroundServiceConnectedDevice"
+ android:label="@string/permlab_foregroundServiceConnectedDevice"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "dataSync".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"
+ android:description="@string/permdesc_foregroundServiceDataSync"
+ android:label="@string/permlab_foregroundServiceDataSync"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "location".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"
+ android:description="@string/permdesc_foregroundServiceLocation"
+ android:label="@string/permlab_foregroundServiceLocation"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "mediaPlayback".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
+ android:description="@string/permdesc_foregroundServiceMediaPlayback"
+ android:label="@string/permlab_foregroundServiceMediaPlayback"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "mediaProjection".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
+ android:description="@string/permdesc_foregroundServiceMediaProjection"
+ android:label="@string/permlab_foregroundServiceMediaProjection"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "microphone".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"
+ android:description="@string/permdesc_foregroundServiceMicrophone"
+ android:label="@string/permlab_foregroundServiceMicrophone"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "phoneCall".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"
+ android:description="@string/permdesc_foregroundServicePhoneCall"
+ android:label="@string/permlab_foregroundServicePhoneCall"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "health".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"
+ android:description="@string/permdesc_foregroundServiceHealth"
+ android:label="@string/permlab_foregroundServiceHealth"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "remoteMessaging".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING"
+ android:description="@string/permdesc_foregroundServiceRemoteMessaging"
+ android:label="@string/permlab_foregroundServiceRemoteMessaging"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "systemExempted".
+ Apps are allowed to use this type only in the use cases listed in
+ {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED}.
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED"
+ android:description="@string/permdesc_foregroundServiceSystemExempted"
+ android:label="@string/permlab_foregroundServiceSystemExempted"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "fileManagement".
+ <p>Protection level: normal|instant
+ @hide
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_FILE_MANAGEMENT"
+ android:description="@string/permdesc_foregroundServiceFileManagement"
+ android:label="@string/permlab_foregroundServiceFileManagement"
+ android:protectionLevel="normal|instant" />
+
+ <!-- @FlaggedApi("android.content.pm.introduce_media_processing_type")
+ Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "mediaProcessing".
+ <p>Protection level: normal|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROCESSING"
+ android:description="@string/permdesc_foregroundServiceMediaProcessing"
+ android:label="@string/permlab_foregroundServiceMediaProcessing"
+ android:protectionLevel="normal|instant" />
+
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground} with the type "specialUse".
+ <p>Protection level: normal|appop|instant
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"
+ android:description="@string/permdesc_foregroundServiceSpecialUse"
+ android:label="@string/permlab_foregroundServiceSpecialUse"
+ android:protectionLevel="normal|appop|instant" />
+
+ <!-- @SystemApi Allows to access all app shortcuts.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_SHORTCUTS"
+ android:protectionLevel="signature|role|recents" />
+
+ <!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
+ @hide -->
+ <permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_RUNTIME_PROFILES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows audio policy management. -->
+ <permission android:name="android.permission.MANAGE_AUDIO_POLICY"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to turn on / off quiet mode.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_QUIET_MODE"
+ android:protectionLevel="signature|privileged|development|role" />
+
+ <!-- Allows internal management of the camera framework
+ @hide -->
+ <permission android:name="android.permission.MANAGE_CAMERA"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to control remote animations. See
+ {@link ActivityOptions#makeRemoteAnimation}
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
+ android:protectionLevel="signature|privileged|recents" />
+
+ <!-- Allows an application to watch changes and/or active state of app ops.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.WATCH_APPOPS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows hidden API checks to be disabled when starting a process.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Permission that protects the
+ {@link android.provider.Telephony.Intents#ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL}
+ broadcast -->
+ <permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission.
+ <p>Protection level: signature -->
+ <permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+ ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Must be required by an {@link android.service.storage.ExternalStorageService} to
+ ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Permission that allows configuring appops.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_APPOPS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Permission that allows background clipboard access.
+ @hide Not for use by third-party applications. -->
+ <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Permission that allows apps to disable the clipboard access notifications.
+ @hide
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MANAGE_CLIPBOARD_ACCESS_NOTIFICATION"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @hide Permission that suppresses the notification when the clipboard is accessed.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows modifying accessibility state.
+ <p> The only approved role for this permission is COMPANION_DEVICE_APP_STREAMING.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_ACCESSIBILITY"
+ android:protectionLevel="signature|setup|recents|role" />
+
+ <!-- @SystemApi Allows an app to grant a profile owner access to device identifiers.
+ <p>Not for use by third-party applications.
+ @deprecated
+ @hide -->
+ <permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an app to mark a profile owner as managing an organization-owned device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MARK_DEVICE_ORGANIZATION_OWNED"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows financial apps to read filtered sms messages.
+ Protection level: signature|appop
+ @deprecated The API that used this permission is no longer functional. -->
+ <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
+ android:protectionLevel="signature|appop" />
+
+ <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#Q} that want to use
+ {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
+ intents}.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+ android:label="@string/permlab_fullScreenIntent"
+ android:description="@string/permdesc_fullScreenIntent"
+ android:protectionLevel="normal|appop" />
+
+ <!-- @SystemApi Required for the privileged assistant apps targeting
+ {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
+ that receive voice trigger from a sandboxed {@link HotwordDetectionService}.
+ <p>Protection level: signature|privileged|appop
+ @FlaggedApi("android.permission.flags.voice_activation_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO"
+ android:protectionLevel="signature|privileged|appop" />
+
+ <!-- @SystemApi Allows requesting the framework broadcast the
+ {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
+ @hide -->
+ <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY}
+ intent.
+ @hide -->
+ <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
+ @hide -->
+ <permission android:name="android.permission.AMBIENT_WALLPAPER"
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- @SystemApi Allows sensor privacy to be modified.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_SENSOR_PRIVACY"
+ android:protectionLevel="internal|role|installer" />
+
+ <!-- @SystemApi Allows sensor privacy changes to be observed.
+ @hide -->
+ <permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY"
+ android:protectionLevel="internal|role|installer" />
+
+ <!-- @SystemApi Permission that protects the {@link Intent#ACTION_REVIEW_ACCESSIBILITY_SERVICES}
+ intent.
+ @hide -->
+ <permission android:name="android.permission.REVIEW_ACCESSIBILITY_SERVICES"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an activity to replace the app name and icon displayed in share targets
+ in the sharesheet for the Q-release and later.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to access shared libraries.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
+ android:protectionLevel="signature|installer" />
+
+ <!-- Allows an app to log compat change usage.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.LOG_COMPAT_CHANGE"
+ android:protectionLevel="signature|privileged" />
+ <!-- Allows an app to read compat change config.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
+ android:protectionLevel="signature|privileged" />
+ <!-- Allows an app to override compat change config.
+ This permission only allows to override config on debuggable builds or test-apks and is
+ therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
+ android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an app to override compat change config on release builds.
+ Only ChangeIds that are annotated as @Overridable can be overridden on release builds.
+ @hide -->
+ <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows input events to be monitored. Very dangerous! @hide -->
+ <permission android:name="android.permission.MONITOR_INPUT"
+ android:protectionLevel="signature|recents" />
+ <!-- @SystemApi Allows the use of FLAG_SLIPPERY, which permits touch events to slip from the
+ current window to the window where the touch currently is on top of. @hide -->
+ <permission android:name="android.permission.ALLOW_SLIPPERY_TOUCHES"
+ android:protectionLevel="signature|privileged|recents|role" />
+ <!-- Allows the caller to change the associations between input devices and displays.
+ Very dangerous! @hide -->
+ <permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY"
+ android:protectionLevel="signature" />
+
+ <!-- Allows query of any normal app on the device, regardless of manifest declarations.
+ <p>Protection level: normal -->
+ <permission android:name="android.permission.QUERY_ALL_PACKAGES"
+ android:label="@string/permlab_queryAllPackages"
+ android:description="@string/permdesc_queryAllPackages"
+ android:protectionLevel="normal" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
+ <!-- @hide Allow the caller to collect debugging data from processes that otherwise
+ would require USAGE_STATS. Before sharing this data with other apps, holders
+ of this permission are REQUIRED to themselves check that the caller has
+ PACKAGE_USAGE_STATS and OP_GET_USAGE_STATS. -->
+ <permission android:name="android.permission.PEEK_DROPBOX_DATA"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to access TV tuner HAL
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_TV_TUNER"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- @SystemApi Allows an application to access descrambler of TV tuner HAL
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_TV_DESCRAMBLER"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- @SystemApi Allows an application to access shared filter of TV tuner HAL
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_TV_SHARED_FILTER"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- Allows an application to create trusted displays. @hide @SystemApi -->
+ <permission android:name="android.permission.ADD_TRUSTED_DISPLAY"
+ android:protectionLevel="signature|role" />
+
+ <!-- Allows an application to create always-unlocked displays. @hide @SystemApi -->
+ <permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY"
+ android:protectionLevel="signature|role"/>
+
+ <!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
+ <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @hide @SystemApi Allows an application to manage app hibernation state. -->
+ <permission android:name="android.permission.MANAGE_APP_HIBERNATION"
+ android:protectionLevel="signature|installer" />
+
+ <!-- @hide @TestApi Allows apps to reset the state of {@link com.android.server.am.AppErrors}.
+ <p>CTS tests will use UiAutomation.adoptShellPermissionIdentity() to gain access. -->
+ <permission android:name="android.permission.RESET_APP_ERRORS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows ThemeOverlayController to delay launch of Home / SetupWizard on boot, ensuring
+ Theme Palettes and Colors are ready -->
+ <permission android:name="android.permission.SET_THEME_OVERLAY_CONTROLLER_READY"
+ android:protectionLevel="signature|setup" />
+
+ <!-- @hide Allows an application to create/destroy input consumer. -->
+ <permission android:name="android.permission.INPUT_CONSUMER"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @TestApi Allows an application to control the system's device state managed by the
+ {@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
+ devices this would grant access to toggle between the folded and unfolded states. -->
+ <permission android:name="android.permission.CONTROL_DEVICE_STATE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @SystemApi Must be required by a
+ {@link android.service.displayhash.DisplayHashingService}
+ to ensure that only the system can bind to it.
+ This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_DISPLAY_HASHING_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
+ <p>Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows managing the Game Mode
+ @hide -->
+ <permission android:name="android.permission.MANAGE_GAME_MODE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @TestApi Allows setting the game service provider, meant for tests only.
+ @hide -->
+ <permission android:name="android.permission.SET_GAME_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows accessing the frame rate per second of a given application
+ @hide -->
+ <permission android:name="android.permission.ACCESS_FPS_COUNTER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows the GameService provider to create GameSession and call GameSession
+ APIs and overlay a view on top of the game's Activity.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_GAME_ACTIVITY"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows the holder to register callbacks to inform the RebootReadinessManager
+ when they are performing reboot-blocking work.
+ @hide -->
+ <permission android:name="android.permission.SIGNAL_REBOOT_READINESS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows the holder to launch an Intent Resolver flow with custom presentation
+ and/or targets.
+ @FlaggedApi("android.nfc.enable_nfc_mainline")
+ @hide -->
+ <permission android:name="android.permission.SHOW_CUSTOMIZED_RESOLVER"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows an application to get a People Tile preview for a given shortcut. -->
+ <permission android:name="android.permission.GET_PEOPLE_TILE_PREVIEW"
+ android:protectionLevel="signature|recents" />
+
+ <!-- @hide @SystemApi Allows an application to retrieve whether shortcut is backed by a
+ Conversation.
+ TODO(b/180412052): STOPSHIP: Define a role so it can be granted to Shell and AiAi. -->
+ <permission android:name="android.permission.READ_PEOPLE_DATA"
+ android:protectionLevel="signature|recents|role"/>
+
+ <!-- @hide @SystemApi Allows a logical component within an application to
+ temporarily renounce a set of otherwise granted permissions. -->
+ <permission android:name="android.permission.RENOUNCE_PERMISSIONS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an application to read nearby streaming policy. The policy controls
+ whether to allow the device to stream its notifications and apps to nearby devices.
+ Applications that are not the device owner will need this permission to call
+ {@link android.app.admin.DevicePolicyManager#getNearbyNotificationStreamingPolicy} or
+ {@link android.app.admin.DevicePolicyManager#getNearbyAppStreamingPolicy}. -->
+ <permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY"
+ android:protectionLevel="normal" />
+
+ <!-- @SystemApi Allows the holder to set the source of the data when setting a clip on the
+ clipboard.
+ @hide -->
+ <permission android:name="android.permission.SET_CLIP_SOURCE"
+ android:protectionLevel="signature|recents" />
+
+ <!-- @SystemApi Allows an application to access TV tuned info
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.ACCESS_TUNED_INFO"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- Allows an application to indicate via
+ {@link android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(int)}
+ that user action should not be required for an app update.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"
+ android:label="@string/permlab_updatePackagesWithoutUserAction"
+ android:description="@string/permdesc_updatePackagesWithoutUserAction"
+ android:protectionLevel="normal" />
+ <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/>
+
+ <!-- Allows an application to indicate via {@link
+ android.content.pm.PackageInstaller.SessionParams#setRequestUpdateOwnership}
+ that it has the intention of becoming the update owner.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.ENFORCE_UPDATE_OWNERSHIP"
+ android:protectionLevel="normal" />
+ <uses-permission android:name="android.permission.ENFORCE_UPDATE_OWNERSHIP" />
+
+
+ <!-- Allows an application to take screenshots of layers that normally would be blacked out when
+ a screenshot is taken. Specifically, layers that have the flag
+ {@link android.view.SurfaceControl#SECURE} will be screenshot if the caller requests to
+ capture secure layers. Normally those layers will be rendered black.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to query over global data in AppSearch.
+ @hide -->
+ <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to query over global data in AppSearch that's visible to the
+ ASSISTANT role. -->
+ <permission android:name="android.permission.READ_ASSISTANT_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to query over global data in AppSearch that's visible to the
+ HOME role. -->
+ <permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an assistive application to perform actions on behalf of users inside of
+ applications.
+ <p>For now, this permission is only granted to the Assistant application selected by
+ the user.
+ <p>Protection level: internal|role
+ -->
+ <permission android:name="android.permission.EXECUTE_APP_ACTION"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to display its suggestions using the autofill framework.
+ <p>For now, this permission is only granted to the Browser application.
+ <p>Protection level: internal|role
+ -->
+ <permission android:name="android.permission.PROVIDE_OWN_AUTOFILL_SUGGESTIONS"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Allows an application to create virtual devices in VirtualDeviceManager.
+ @hide -->
+ <permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Must be required by a safety source to send an update using the
+ {@link android.safetycenter.SafetyCenterManager}.
+ <p>Protection level: internal|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE"
+ android:protectionLevel="internal|privileged" />
+
+ <!-- @SystemApi Allows an application to launch device manager setup screens.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to update certain device management related system
+ resources.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an app to read whether SafetyCenter is enabled/disabled.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.READ_SAFETY_CENTER_STATUS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Required to access the safety center internal APIs using the
+ {@link android.safetycenter.SafetyCenterManager}.
+ <p>Protection level: internal|installer|role
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_SAFETY_CENTER"
+ android:protectionLevel="internal|installer|role" />
+
+ <!-- @SystemApi Allows an application to access the AmbientContextEvent service.
+ @hide
+ -->
+ <permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"
+ android:protectionLevel="signature|privileged|role"/>
+
+ <!-- @SystemApi Required by a AmbientContextEventDetectionService
+ to ensure that only the service with this permission can bind to it.
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an app to set keep-clear areas without restrictions on the size or
+ number of keep-clear areas (see {@link android.view.View#setPreferKeepClearRects}).
+ When the system arranges floating windows onscreen, it might decide to ignore keep-clear
+ areas from windows, whose owner does not have this permission.
+ @hide
+ -->
+ <permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an app to set gesture exclusion without restrictions on the vertical extent of the
+ exclusions (see {@link android.view.View#setSystemGestureExclusionRects}).
+ @hide
+ -->
+ <permission android:name="android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION"
+ android:protectionLevel="signature|privileged|recents" />
+
+ <!-- @SystemApi Allows TV input apps and TV apps to use TIS extension interfaces for
+ domain-specific features.
+ <p>Protection level: signature|privileged|vendorPrivileged
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.TIS_EXTENSION_INTERFACE"
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+ <!-- @SystemApi Allows an application to write to the security log buffer in logd.
+ @hide -->
+ <permission android:name="android.permission.WRITE_SECURITY_LOG"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows an UID to be visible to the application based on an interaction between the
+ two apps. This permission is not intended to be held by apps.
+ @hide @TestApi @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) -->
+ <permission android:name="android.permission.MAKE_UID_VISIBLE"
+ android:protectionLevel="signature" />
+
+ <!-- Limits the system as the only handler of the QUERY_PACKAGE_RESTART broadcast
+ @hide -->
+ <permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART"
+ android:protectionLevel="signature" />
+
+ <!-- Allows low-level access to re-mapping modifier keys.
+ <p>Not for use by third-party applications.
+ @hide
+ @TestApi -->
+ <permission android:name="android.permission.REMAP_MODIFIER_KEYS"
+ android:protectionLevel="signature" />
+
+ <!-- Allows low-level access to monitor keyboard backlight changes.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT"
+ android:protectionLevel="signature" />
+
+ <!-- Allows low-level access to monitor sticky modifier state changes when A11Y Sticky keys
+ feature is enabled.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE"
+ android:protectionLevel="signature" />
+
+ <uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />
+
+ <!-- Allows financed device kiosk apps to perform actions on the Device Lock service
+ <p>Protection level: internal|role
+ <p>Intended for use by the FINANCED_DEVICE_KIOSK role only.
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_LOCK_STATE"
+ android:protectionLevel="internal|role" />
+
+ <!-- @SystemApi Required by a WearableSensingService to
+ ensure that only the caller with this permission can bind to it.
+ <p> Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.BIND_WEARABLE_SENSING_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an app to manage the wearable sensing service.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_WEARABLE_SENSING_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an app to use the on-device intelligence service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.USE_ON_DEVICE_INTELLIGENCE"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- @SystemApi Allows an app to bind the on-device intelligence service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- @SystemApi Allows an app to bind the on-device sandboxed service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE"
+ android:protectionLevel="signature"/>
+
+
+ <!-- Allows applications to use the user-initiated jobs API. For more details
+ see {@link android.app.job.JobInfo.Builder#setUserInitiated}.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.RUN_USER_INITIATED_JOBS"
+ android:protectionLevel="normal"/>
+
+ <!-- Allows an app access to the installer provided app metadata.
+ @SystemApi
+ @hide
+ -->
+ <permission android:name="android.permission.GET_APP_METADATA"
+ android:protectionLevel="signature|installer|verifier" />
+
+ <!-- @hide @SystemApi Allows an application to stage HealthConnect's remote data so that
+ HealthConnect can later integrate it. -->
+ <permission android:name="android.permission.STAGE_HEALTH_CONNECT_REMOTE_DATA"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_healthConnectRestoreKnownSigners"/>
+
+ <!-- @hide @TestApi Allows an application to clear HealthConnect's staged remote data for
+ testing only. For security reasons, this is a platform-only permission. -->
+ <permission android:name="android.permission.DELETE_STAGED_HEALTH_CONNECT_REMOTE_DATA"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @TestApi Allows tests running in CTS-in-sandbox mode to launch activities -->
+ <permission android:name="android.permission.START_ACTIVITIES_FROM_SDK_SANDBOX"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows the holder to call health connect migration APIs.
+ @hide -->
+ <permission android:name="android.permission.MIGRATE_HEALTH_CONNECT_DATA"
+ android:protectionLevel="signature|knownSigner"
+ android:knownCerts="@array/config_healthConnectMigrationKnownSigners" />
+
+ <!-- @SystemApi Allows an app to query apps in clone profile. The permission is
+ bidirectional in nature, i.e. cloned apps would be able to query apps in root user.
+ The permission is not meant for 3P apps as of now.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.QUERY_CLONED_APPS"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide @SystemApi
+ Allows applications to capture bugreport directly without consent dialog when using the
+ bugreporting API on userdebug/eng build.
+ <p>The application still needs to hold {@link android.permission.DUMP} permission and be
+ bugreport-whitelisted to be able to capture a bugreport using the bugreporting API in the
+ first place. Then, when the corresponding app op of this permission is ALLOWED, the
+ bugreport can be captured directly without going through the consent dialog.
+ <p>Protection level: internal|appop
+ <p>Intended to only be used on userdebug/eng build.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD"
+ android:protectionLevel="internal|appop" />
+
+ <!-- @hide @SystemApi
+ @FlaggedApi(android.os.Flags.FLAG_ALLOW_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT)
+ Allows system applications to capture bugreport directly without consent dialog when using
+ the bugreporting API, and instead use the applications-owned consent page.
+ <p>The application still needs to hold {@link android.permission.DUMP} permission and be
+ bugreport-whitelisted to be able to capture a bugreport using the bugreporting API in the
+ first place.
+ <p>Protection level: signature|privileged
+ <p>Not for use by third-party applications. -->
+ <permission
+ android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows to call APIs that log process lifecycle events
+ @hide -->
+ <permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"
+ android:protectionLevel="signature|module" />
+
+ <!-- @hide Allows the settings app to access GPU service APIs".
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.ACCESS_GPU_SERVICE"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows an application to get type of any provider uri.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.GET_ANY_PROVIDER_TYPE"
+ android:protectionLevel="signature" />
+
+
+ <!-- @hide Allows internal applications to read and synchronize non-core flags.
+ Apps without this permission can only read a subset of flags specifically intended
+ for use in "core", (i.e. third party apps). Apps with this permission can define their
+ own flags, and federate those values with other system-level apps.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.SYNC_FLAGS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide Allows internal applications to override flags in the FeatureFlags service.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.WRITE_FLAGS"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @SystemApi
+ @FlaggedApi("android.app.get_binding_uid_importance")
+ Allows to get the importance of an UID that has a service
+ binding to the app.
+ <p>Protection level: signature|privileged
+ -->
+ <permission android:name="android.permission.GET_BINDING_UID_IMPORTANCE"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @hide Allows internal applications to manage displays.
+ <p>This means intercept internal signals about displays being (dis-)connected
+ and being able to enable or disable the external displays.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.MANAGE_DISPLAYS"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an app to track all preparations for a complete factory reset.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("android.permission.flags.factory_reset_prep_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.PREPARE_FACTORY_RESET"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows focused window to override the default behavior of supported system keys.
+ The following keycodes are supported:
+ <p> KEYCODE_STEM_PRIMARY
+ <p>If an app is granted this permission and has a focused window, it will be allowed to
+ receive supported key events that are otherwise handled by the system. The app can choose
+ to consume the key events and trigger its own behavior, in which case the default key
+ behavior will be skipped.
+ <p>For example, KEYCODE_STEM_PRIMARY by default opens recent app launcher. If the foreground
+ fitness app is granted this permission, it can repurpose the KEYCODE_STEM_PRIMARY button
+ to pause/resume the current fitness session.
+ <p>Protection level: signature|privileged
+ @FlaggedApi("com.android.input.flags.override_key_behavior_permission_apis")
+ @hide -->
+ <permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Allows internal applications to restrict display modes
+ <p>Protection level: signature
+ @FlaggedApi("com.android.server.display.feature.flags.enable_restrict_display_modes")
+ @hide
+ -->
+ <permission android:name="android.permission.RESTRICT_DISPLAY_MODES"
+ android:protectionLevel="signature" />
+
+ <!-- @hide @SystemApi
+ @FlaggedApi("com.android.server.notification.flags.redact_otp_notifications_from_untrusted_listeners")
+ Allows apps with a NotificationListenerService to receive notifications with sensitive
+ information
+ <p>Apps with a NotificationListenerService without this permission will not be able
+ to view certain types of sensitive information contained in notifications
+ <p>Protection level: signature|role
+ -->
+ <permission android:name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi
+ @FlaggedApi("android.app.bic_client")
+ Allows app to call BackgroundInstallControlManager API to retrieve silently installed apps
+ for all users on device.
+ <p>Apps with a BackgroundInstallControlManager client will not be able to call any API without
+ this permission.
+ <p>Protection level: signature|role
+ @hide
+ -->
+ <permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES"
+ android:protectionLevel="signature|role" />
+
+ <!-- @SystemApi Allows an application to read the system grammatical gender.
+ @FlaggedApi("android.app.system_terms_of_address_enabled")
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- @SystemApi
+ @FlaggedApi("android.content.pm.emergency_install_permission")
+ Allows each app store in the system image to designate another app in the system image to
+ update the app store
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.EMERGENCY_INSTALL_PACKAGES"
+ android:protectionLevel="signature|privileged"/>
+
+ <!-- Allows internal applications to override screen timeout temporarily
+ <p>Protection level: signature
+ <p>Not for use by third-party applications.
+ @FlaggedApi("com.android.server.power.feature.flags.enable_early_screen_timeout_detector")
+ @hide
+ -->
+ <permission android:name="android.permission.SCREEN_TIMEOUT_OVERRIDE"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi
+ @FlaggedApi("android.security.fsverity_api")
+ Allows app to setup fs-verity through FileIntegrityManager.
+ <p>Protection level: signature|privileged
+ @hide
+ -->
+ <permission android:name="android.permission.SETUP_FSVERITY"
+ android:protectionLevel="signature|privileged"/>
+
+ <!--
+ @TestApi
+ Signature permission reserved for testing. This should never be used to
+ gate any actual functionality.
+ <p>
+ Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
+ android:protectionLevel="signature"/>
+
+ <!-- Attribution for Geofencing service. -->
+ <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
+ <!-- Attribution for Country Detector. -->
+ <attribution android:tag="CountryDetector" android:label="@string/country_detector"/>
+ <!-- Attribution for Location service. -->
+ <attribution android:tag="LocationService" android:label="@string/location_service"/>
+ <!-- Attribution for Gnss service. -->
+ <attribution android:tag="GnssService" android:label="@string/gnss_service"/>
+ <!-- Attribution for Sensor Notification service. -->
+ <attribution android:tag="SensorNotificationService"
+ android:label="@string/sensor_notification_service"/>
+ <!-- Attribution for Twilight service. -->
+ <attribution android:tag="TwilightService" android:label="@string/twilight_service"/>
+ <!-- Attribution for Gnss Time Update service. -->
+ <attribution android:tag="GnssTimeUpdateService"
+ android:label="@string/gnss_time_update_service"/>
+ <!-- Attribution for MusicRecognitionManagerService.
+ <p>Not for use by third-party applications.</p> -->
+ <attribution android:tag="MusicRecognitionManagerService"
+ android:label="@string/music_recognition_manager_service"/>
+ <!-- Attribution for Device Policy Manager service. -->
+ <attribution android:tag="DevicePolicyManagerService"
+ android:label="@string/device_policy_manager_service"/>
+
+ <application android:process="system"
+ android:persistent="true"
+ android:hasCode="false"
+ android:label="@string/android_system_label"
+ android:allowClearUserData="false"
+ android:backupAgent="com.android.server.backup.SystemBackupAgent"
+ android:killAfterRestore="false"
+ android:icon="@drawable/ic_launcher_android"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.DeviceDefault.Light.DarkActionBar"
+ android:defaultToDeviceProtectedStorage="true"
+ android:forceQueryable="true"
+ android:directBootAware="true">
+ <activity android:name="com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity"
+ android:exported="false"
+ android:theme="@style/Theme.DeviceDefault.Dialog.Alert.DayNight"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
+ <intent-filter>
+ <action android:name="com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity"
+ android:exported="false"
+ android:theme="@style/Theme.DeviceDefault.Resolver"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
+ <intent-filter>
+ <action android:name="com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.internal.app.NfcResolverActivity"
+ android:theme="@style/Theme.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:multiprocess="true"
+ android:permission="android.permission.SHOW_CUSTOMIZED_RESOLVER"
+ android:exported="true">
+ <intent-filter android:priority="100" >
+ <action android:name="android.nfc.action.SHOW_NFC_RESOLVER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.internal.app.IntentForwarderActivity"
+ android:finishOnCloseSystemDialogs="true"
+ android:theme="@style/Theme.DeviceDefault.Resolver"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:label="@string/user_owner_label"
+ android:exported="true"
+ android:visibleToInstantApps="true"
+ >
+ </activity>
+ <activity-alias android:name="com.android.internal.app.ForwardIntentToParent"
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:exported="true"
+ android:label="@string/user_owner_label">
+ </activity-alias>
+ <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
+ android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+ android:icon="@drawable/ic_corp_badge"
+ android:exported="true"
+ android:label="@string/managed_profile_label">
+ </activity-alias>
+ <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
+ android:theme="@style/Theme.DeviceDefault.System.Dialog.Alert"
+ android:label="@string/heavy_weight_switcher_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+ <activity android:name="com.android.internal.app.PlatLogoActivity"
+ android:theme="@style/Theme.NoTitleBar.Fullscreen"
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
+ android:enableOnBackInvokedCallback="true"
+ android:icon="@drawable/platlogo"
+ android:process=":ui">
+ </activity>
+ <activity android:name="com.android.internal.app.DisableCarModeActivity"
+ android:theme="@style/Theme.NoDisplay"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="android.accounts.ChooseAccountActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
+ </activity>
+
+ <activity android:name="android.accounts.ChooseTypeAndAccountActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
+ </activity>
+
+ <activity android:name="android.accounts.ChooseAccountTypeActivity"
+ android:excludeFromRecents="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/choose_account_label"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
+ </activity>
+
+ <activity android:name="android.accounts.CantAddAccountActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.NoActionBar"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="android.accounts.GrantCredentialsPermissionActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge"
+ android:process=":ui"
+ android:visibleToInstantApps="true">
+ </activity>
+
+ <activity android:name="android.content.SyncActivityTooManyDeletes"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog"
+ android:label="@string/sync_too_many_deletes"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.internal.app.ShutdownActivity"
+ android:permission="android.permission.SHUTDOWN"
+ android:theme="@style/Theme.NoDisplay"
+ android:exported="true"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="com.android.internal.intent.action.REQUEST_SHUTDOWN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.REBOOT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="com.android.internal.app.SystemUserHomeActivity"
+ android:enabled="false"
+ android:process=":ui"
+ android:systemUserOnly="true"
+ android:exported="true"
+ android:theme="@style/Theme.Translucent.NoTitleBar">
+ <intent-filter android:priority="-100">
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ </intent-filter>
+ </activity>
+
+ <!-- Activity to prompt user if it's ok to create a new user sandbox for a
+ specified account. -->
+ <activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:exported="true"
+ android:theme="@style/Theme.Dialog.Confirmation">
+ <intent-filter android:priority="1000">
+ <action android:name="android.os.action.CREATE_USER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="com.android.internal.app.SuspendedAppActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.internal.app.SetScreenLockDialogActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.internal.app.BlockedAppActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:lockTaskMode="always"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.internal.app.BlockedAppStreamingActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.internal.app.LaunchAfterAuthenticationActivity"
+ android:theme="@style/Theme.Translucent.NoTitleBar"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
+ <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true">
+ </activity>
+
+ <activity android:name="com.android.internal.app.HarmfulAppWarningActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:label="@string/harmful_app_warning_title"
+ android:exported="false">
+ </activity>
+
+ <activity android:name="com.android.server.notification.NASLearnMoreActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:exported="false">
+ </activity>
+
+ <activity android:name="android.service.games.GameSessionTrampolineActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_GAME_ACTIVITY"
+ android:theme="@style/Theme.GameSessionTrampoline">
+ </activity>
+
+ <receiver android:name="com.android.server.BootReceiver"
+ android:exported="true"
+ android:systemUserOnly="true">
+ <intent-filter android:priority="1000">
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.CertPinInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_PINS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.NetworkWatchlistInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_NETWORK_WATCHLIST" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.ApnDbInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="com.android.internal.intent.action.UPDATE_APN_DB" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.CarrierProvisioningUrlsInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_CT_LOGS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.LangIdInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_LANG_ID" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.SmartSelectionInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_SMART_SELECTION" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.ConversationActionsInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.intent.action.UPDATE_CONVERSATION_ACTIONS" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.CarrierIdInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.os.action.UPDATE_CARRIER_ID_DB" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.updates.EmergencyNumberDbInstallReceiver"
+ android:exported="true"
+ android:permission="android.permission.UPDATE_CONFIG">
+ <intent-filter>
+ <action android:name="android.os.action.UPDATE_EMERGENCY_NUMBER_DB" />
+ <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.MasterClearReceiver"
+ android:exported="true"
+ android:permission="android.permission.MASTER_CLEAR">
+ <intent-filter
+ android:priority="100" >
+ <!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
+ <action android:name="android.intent.action.FACTORY_RESET" />
+ <!-- As above until all the references to the deprecated MASTER_CLEAR get updated to
+ FACTORY_RESET. -->
+ <action android:name="android.intent.action.MASTER_CLEAR" />
+
+ <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
+ <action android:name="com.google.android.c2dm.intent.RECEIVE" />
+ <category android:name="android.intent.category.MASTER_CLEAR" />
+ </intent-filter>
+ </receiver>
+
+ <receiver android:name="com.android.server.WallpaperUpdateReceiver"
+ android:exported="true"
+ android:permission="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY">
+ <intent-filter>
+ <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+ </intent-filter>
+ </receiver>
+
+ <!-- Broadcast Receiver listens to sufficient verifier broadcast from Package Manager
+ when installing new SDK. Verification of SDK code during installation time is run
+ to determine compatibility with privacy sandbox restrictions. -->
+ <receiver android:name="com.android.server.sdksandbox.SdkSandboxVerifierReceiver"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION"/>
+ </intent-filter>
+ </receiver>
+
+ <service android:name="android.hardware.location.GeofenceHardwareService"
+ android:permission="android.permission.LOCATION_HARDWARE"
+ android:exported="false" />
+
+ <service android:name="com.android.server.MountServiceIdler"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.SmartStorageMaintIdler"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.ZramWriteback"
+ android:exported="false"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.backup.FullBackupJob"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.backup.KeyValueBackupJob"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.content.SyncJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.pm.BackgroundDexOptJobService"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.pm.DynamicCodeLoggingService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.selinux.SelinuxAuditLogsService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.compos.IsolatedCompilationJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.system.virtualmachine.SecretkeeperJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.PruneInstantAppsJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.storage.DiskStatsLoggingService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.PreloadsFileCacheExpirationJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.camera.CameraStatsJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.usage.UsageStatsIdleService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.display.BrightnessIdleJob"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.people.data.DataMaintenanceService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.profcollect.ProfcollectForwardingService$ProfcollectBGJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.pm.GentleUpdateHelper$Service"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service
+ android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:visibleToInstantApps="true"
+ android:exported="true">
+ <meta-data
+ android:name="android.accessibilityservice"
+ android:resource="@xml/autofill_compat_accessibility_service" />
+ </service>
+
+ <service android:name="com.android.server.blob.BlobStoreIdleJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.companion.association.InactiveAssociationsRemovalService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.appsearch.contactsindexer.ContactsIndexerMaintenanceService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.BinaryTransparencyService$UpdateMeasurementsJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.notification.ReviewNotificationPermissionsJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.notification.NotificationHistoryJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.notification.NotificationBitmapJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.healthconnect.HealthConnectDailyService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.healthconnect.migration.MigrationBroadcastJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.healthconnect.backuprestore.BackupRestore$BackupRestoreJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE">
+ </service>
+
+ <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.LOAD_DATA"/>
+ </intent-filter>
+ </service>
+
+ <!-- TODO: Move to ExtServices or relevant component. -->
+ <service android:name="android.service.selectiontoolbar.DefaultSelectionToolbarRenderService"
+ android:permission="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:process=":ui"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.service.selectiontoolbar.SelectionToolbarRenderService"/>
+ </intent-filter>
+ </service>
+
+ <service android:name="com.android.server.art.BackgroundDexoptJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncInCallService"
+ android:permission="android.permission.BIND_INCALL_SERVICE"
+ android:exported="true">
+ <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS"
+ android:value="true" />
+ <intent-filter>
+ <action android:name="android.telecom.InCallService"/>
+ </intent-filter>
+ </service>
+
+ <service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncConnectionService"
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService"/>
+ </intent-filter>
+ </service>
+
+ <provider
+ android:name="com.android.server.textclassifier.IconsContentProvider"
+ android:authorities="com.android.textclassifier.icons"
+ android:singleUser="true"
+ android:enabled="true"
+ android:exported="true">
+ </provider>
+
+ <meta-data
+ android:name="com.android.server.patch.25239169"
+ android:value="true" />
+
+ </application>
+
+</manifest>
diff --git a/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml
new file mode 100644
index 000000000..783cd7f6b
--- /dev/null
+++ b/tests/cts/permissionpolicy/res/raw/automotive_android_manifest.xml
@@ -0,0 +1,623 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" >
+
+ <!--
+ This file contains the permissions defined by CarService-Builtin(com.android.car)
+ and CarService-updatable(com.[google.]?android.car.updatable). As this is only a
+ resource file, permissions from both packages can be added here.
+ -->
+ <permission-group android:name="android.car.permission-group.CAR_MONITORING"
+ android:icon="@drawable/perm_group_car"
+ android:description="@string/car_permission_desc"
+ android:label="@string/car_permission_label"/>
+ <permission android:name="android.car.permission.CAR_ENERGY"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_energy"
+ android:description="@string/car_permission_desc_energy"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_ENERGY"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_energy"
+ android:description="@string/car_permission_desc_control_car_energy"/>
+ <permission android:name="android.car.permission.READ_DRIVER_MONITORING_SETTINGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_driver_monitoring_settings"
+ android:description="@string/car_permission_desc_read_driver_monitoring_settings"/>
+ <permission android:name="android.car.permission.CONTROL_DRIVER_MONITORING_SETTINGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_driver_monitoring_settings"
+ android:description="@string/car_permission_desc_control_driver_monitoring_settings"/>
+ <permission android:name="android.car.permission.READ_DRIVER_MONITORING_STATES"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_driver_monitoring_states"
+ android:description="@string/car_permission_desc_read_driver_monitoring_states"/>
+ <permission android:name="android.car.permission.ADJUST_RANGE_REMAINING"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_adjust_range_remaining"
+ android:description="@string/car_permission_desc_adjust_range_remaining"/>
+ <permission android:name="android.car.permission.CAR_IDENTIFICATION"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_identification"
+ android:description="@string/car_permission_desc_car_identification"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_CLIMATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_hvac"
+ android:description="@string/car_permission_desc_hvac"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_DOORS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_doors"
+ android:description="@string/car_permission_desc_control_car_doors"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_WINDOWS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_windows"
+ android:description="@string/car_permission_desc_control_car_windows"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_MIRRORS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_mirrors"
+ android:description="@string/car_permission_desc_control_car_mirrors"/>
+ <permission android:name="android.car.permission.CONTROL_GLOVE_BOX"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_glove_box"
+ android:description="@string/car_permission_desc_control_glove_box"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_SEATS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_seats"
+ android:description="@string/car_permission_desc_control_car_seats"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_AIRBAGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_airbags"
+ android:description="@string/car_permission_desc_control_car_airbags"/>
+ <permission android:name="android.car.permission.CAR_MILEAGE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_mileage"
+ android:description="@string/car_permission_desc_mileage"/>
+ <permission android:name="android.car.permission.CAR_TIRES"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_tires"
+ android:description="@string/car_permission_desc_car_tires"/>
+ <permission android:name="android.car.permission.READ_CAR_STEERING"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_steering"
+ android:description="@string/car_permission_desc_car_steering"/>
+ <permission android:name="android.car.permission.READ_CAR_DISPLAY_UNITS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_read_car_display_units"
+ android:description="@string/car_permission_desc_read_car_display_units"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_DISPLAY_UNITS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_control_car_display_units"
+ android:description="@string/car_permission_desc_control_car_display_units"/>
+ <permission android:name="android.car.permission.CAR_SPEED"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_speed"
+ android:description="@string/car_permission_desc_speed"/>
+ <permission android:name="android.car.permission.CAR_ENERGY_PORTS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_energy_ports"
+ android:description="@string/car_permission_desc_car_energy_ports"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_ENERGY_PORTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_energy_ports"
+ android:description="@string/car_permission_desc_control_car_energy_ports"/>
+ <permission android:name="android.car.permission.CAR_ENGINE_DETAILED"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_engine_detailed"
+ android:description="@string/car_permission_desc_car_engine_detailed"/>
+ <permission android:name="android.car.permission.CAR_DYNAMICS_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_vehicle_dynamics_state"
+ android:description="@string/car_permission_desc_vehicle_dynamics_state"/>
+ <permission android:name="android.car.permission.CAR_VENDOR_EXTENSION"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_vendor_extension"
+ android:description="@string/car_permission_desc_vendor_extension"/>
+ <permission android:name="android.car.permission.CAR_PROJECTION"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_projection"
+ android:description="@string/car_permission_desc_projection"/>
+ <permission android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_access_projection_status"
+ android:description="@string/car_permission_desc_access_projection_status"/>
+ <permission android:name="android.car.permission.BIND_PROJECTION_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_projection_service"
+ android:description="@string/car_permission_desc_bind_projection_service"/>
+ <permission android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_mock_vehicle_hal"
+ android:description="@string/car_permission_desc_mock_vehicle_hal"/>
+ <permission android:name="android.car.permission.CAR_INFO"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_info"
+ android:description="@string/car_permission_desc_car_info"/>
+ <permission android:name="android.car.permission.PRIVILEGED_CAR_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_privileged_car_info"
+ android:description="@string/car_permission_desc_privileged_car_info"/>
+ <permission android:name="android.car.permission.READ_CAR_VENDOR_PERMISSION_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_vendor_permission_info"
+ android:description="@string/car_permission_desc_vendor_permission_info"/>
+ <permission android:name="android.car.permission.MANAGE_REMOTE_DEVICE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_manage_remote_device"
+ android:description="@string/car_permission_desc_manage_remote_device"/>
+ <permission android:name="android.car.permission.MANAGE_OCCUPANT_CONNECTION"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_manage_occupant_connection"
+ android:description="@string/car_permission_desc_manage_occupant_connection"/>
+
+ <!-- Allows an application to read the vehicle exterior environment information. For example,
+ it allows an application to read the vehicle exterior temperature and night mode status.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_exterior_environment"
+ android:description="@string/car_permission_desc_car_exterior_environment"/>
+ <permission android:name="android.car.permission.CAR_EPOCH_TIME"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_epoch_time"
+ android:description="@string/car_permission_desc_car_epoch_time"/>
+ <permission android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_exterior_lights"
+ android:description="@string/car_permission_desc_car_exterior_lights"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_exterior_lights"
+ android:description="@string/car_permission_desc_control_car_exterior_lights"/>
+ <permission android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_interior_lights"
+ android:description="@string/car_permission_desc_car_interior_lights"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_INTERIOR_LIGHTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_interior_lights"
+ android:description="@string/car_permission_desc_control_car_interior_lights"/>
+ <permission android:name="android.car.permission.CAR_POWER"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:label="@string/car_permission_label_car_power"
+ android:description="@string/car_permission_desc_car_power"/>
+ <permission android:name="android.car.permission.CAR_POWERTRAIN"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_powertrain"
+ android:description="@string/car_permission_desc_car_powertrain"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_POWERTRAIN"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_powertrain"
+ android:description="@string/car_permission_desc_control_car_powertrain"/>
+ <permission android:name="android.car.permission.READ_CAR_SEAT_BELTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_car_seat_belts"
+ android:description="@string/car_permission_desc_read_car_seat_belts"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_DYNAMICS_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_dynamics_state"
+ android:description="@string/car_permission_desc_control_car_dynamics_state"/>
+ <permission android:name="android.car.permission.READ_IMPACT_SENSORS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_impact_sensors"
+ android:description="@string/car_permission_desc_read_impact_sensors"/>
+ <permission android:name="android.car.permission.READ_HEAD_UP_DISPLAY_STATUS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_head_up_display_status"
+ android:description="@string/car_permission_desc_read_head_up_display_status"/>
+ <permission android:name="android.car.permission.CONTROL_HEAD_UP_DISPLAY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_head_up_display"
+ android:description="@string/car_permission_desc_control_head_up_display"/>
+ <permission android:name="android.car.permission.READ_VALET_MODE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_valet_mode"
+ android:description="@string/car_permission_desc_read_valet_mode"/>
+ <permission android:name="android.car.permission.CONTROL_VALET_MODE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_valet_mode"
+ android:description="@string/car_permission_desc_control_valet_mode"/>
+ <permission android:name="android.car.permission.READ_CAR_AIRBAGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_car_airbags"
+ android:description="@string/car_permission_desc_read_car_airbags"/>
+ <permission android:name="android.car.permission.READ_ULTRASONICS_SENSOR_DATA"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_ultrasonics_sensor_data"
+ android:description="@string/car_permission_desc_read_ultrasonics_sensor_data"/>
+ <permission android:name="android.car.permission.CAR_NAVIGATION_MANAGER"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_car_navigation_manager"
+ android:description="@string/car_permission_desc_car_navigation_manager"/>
+ <permission android:name="android.car.permission.CAR_DIAGNOSTICS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_diag_read"
+ android:description="@string/car_permission_desc_diag_read"/>
+ <permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_diag_clear"
+ android:description="@string/car_permission_desc_diag_clear"/>
+ <permission android:name="android.car.permission.BIND_VMS_CLIENT"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_vms_client"
+ android:description="@string/car_permission_desc_bind_vms_client"/>
+ <permission android:name="android.car.permission.VMS_PUBLISHER"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_vms_publisher"
+ android:description="@string/car_permission_desc_vms_publisher"/>
+ <permission android:name="android.car.permission.VMS_SUBSCRIBER"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_vms_subscriber"
+ android:description="@string/car_permission_desc_vms_subscriber"/>
+ <permission android:name="android.car.permission.CAR_DRIVING_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_driving_state"
+ android:description="@string/car_permission_desc_driving_state"/>
+ <permission android:name="android.car.permission.USE_CAR_TELEMETRY_SERVICE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_use_telemetry_service"
+ android:description="@string/car_permission_desc_use_telemetry_service"/>
+ <permission android:name="android.car.permission.REQUEST_CAR_EVS_ACTIVITY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_request_evs_activity"
+ android:description="@string/car_permission_desc_request_evs_activity"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_evs_activity"
+ android:description="@string/car_permission_desc_control_evs_activity"/>
+ <permission android:name="android.car.permission.USE_CAR_EVS_CAMERA"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_use_evs_camera"
+ android:description="@string/car_permission_desc_use_evs_camera"/>
+ <permission android:name="android.car.permission.MONITOR_CAR_EVS_STATUS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_monitor_evs_status"
+ android:description="@string/car_permission_desc_monitor_evs_status"/>
+ <permission android:name="android.car.permission.CONTROL_APP_BLOCKING"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_app_blocking"
+ android:description="@string/car_permission_desc_control_app_blocking"/>
+ <permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_audio_volume"
+ android:description="@string/car_permission_desc_audio_volume"/>
+ <permission android:name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_audio_settings"
+ android:description="@string/car_permission_desc_audio_settings"/>
+ <permission android:name="android.car.permission.RECEIVE_CAR_AUDIO_DUCKING_EVENTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_receive_ducking"
+ android:description="@string/car_permission_desc_receive_ducking"/>
+ <permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
+ android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
+ <permission android:name="android.car.permission.BIND_CAR_INPUT_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_input_service"
+ android:description="@string/car_permission_desc_bind_input_service"/>
+ <permission android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_car_display_in_cluster"
+ android:description="@string/car_permission_desc_car_display_in_cluster"/>
+ <permission android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_car_cluster_control"
+ android:description="@string/car_permission_desc_car_cluster_control"/>
+ <permission android:name="android.car.permission.CAR_MONITOR_CLUSTER_NAVIGATION_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_car_monitor_cluster_navigation_state"
+ android:description="@string/car_permission_desc_car_monitor_cluster_navigation_state"/>
+ <permission android:name="android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_handle_usb_aoap_device"
+ android:description="@string/car_permission_desc_car_handle_usb_aoap_device"/>
+ <permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_ux_restrictions_configuration"
+ android:description="@string/car_permission_desc_car_ux_restrictions_configuration"/>
+ <permission android:name="android.car.permission.READ_CAR_OCCUPANT_AWARENESS_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_car_occupant_awareness_state"
+ android:description="@string/car_permission_desc_read_car_occupant_awareness_state"/>
+ <permission android:name="android.car.permission.ACCESS_PRIVATE_DISPLAY_ID"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_access_private_display_id"
+ android:description="@string/car_permission_desc_access_private_display_id"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_OCCUPANT_AWARENESS_SYSTEM"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_occupant_awareness_system"
+ android:description="@string/car_permission_desc_control_car_occupant_awareness_system"/>
+ <permission android:name="android.car.permission.STORAGE_MONITORING"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_storage_monitoring"
+ android:description="@string/car_permission_desc_storage_monitoring"/>
+ <permission android:name="android.car.permission.CAR_ENROLL_TRUST"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_enroll_trust"
+ android:description="@string/car_permission_desc_enroll_trust"/>
+ <permission android:name="android.car.permission.CAR_TEST_SERVICE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_car_test_service"
+ android:description="@string/car_permission_desc_car_test_service"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_FEATURES"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_features"
+ android:description="@string/car_permission_desc_control_car_features"/>
+ <permission android:name="android.car.permission.USE_CAR_WATCHDOG"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_use_car_watchdog"
+ android:description="@string/car_permission_desc_use_car_watchdog"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_WATCHDOG_CONFIG"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_watchdog_config"
+ android:description="@string/car_permission_desc_control_car_watchdog_config"/>
+ <permission android:name="android.car.permission.COLLECT_CAR_WATCHDOG_METRICS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_collect_car_watchdog_metrics"
+ android:description="@string/car_permission_desc_collect_car_watchdog_metrics"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_WINDOW"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_window"
+ android:description="@string/car_permission_desc_get_car_vendor_category_window"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_WINDOW"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_window"
+ android:description="@string/car_permission_desc_set_car_vendor_category_window"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_DOOR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_door"
+ android:description="@string/car_permission_desc_get_car_vendor_category_door"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_DOOR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_door"
+ android:description="@string/car_permission_desc_set_car_vendor_category_door"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_SEAT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_seat"
+ android:description="@string/car_permission_desc_get_car_vendor_category_seat"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_SEAT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_seat"
+ android:description="@string/car_permission_desc_set_car_vendor_category_seat"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_MIRROR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_mirror"
+ android:description="@string/car_permission_desc_get_car_vendor_category_mirror"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_MIRROR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_mirror"
+ android:description="@string/car_permission_desc_set_car_vendor_category_mirror"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_info"
+ android:description="@string/car_permission_desc_get_car_vendor_category_info"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_info"
+ android:description="@string/car_permission_desc_set_car_vendor_category_info"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_ENGINE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_engine"
+ android:description="@string/car_permission_desc_get_car_vendor_category_engine"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_ENGINE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_engine"
+ android:description="@string/car_permission_desc_set_car_vendor_category_engine"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_HVAC"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_hvac"
+ android:description="@string/car_permission_desc_get_car_vendor_category_hvac"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_HVAC"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_hvac"
+ android:description="@string/car_permission_desc_set_car_vendor_category_hvac"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_LIGHT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_light"
+ android:description="@string/car_permission_desc_get_car_vendor_category_light"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_LIGHT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_light"
+ android:description="@string/car_permission_desc_set_car_vendor_category_light"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_1"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_1"
+ android:description="@string/car_permission_desc_get_car_vendor_category_1"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_1"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_1"
+ android:description="@string/car_permission_desc_set_car_vendor_category_1"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_2"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_2"
+ android:description="@string/car_permission_desc_get_car_vendor_category_2"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_2"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_2"
+ android:description="@string/car_permission_desc_set_car_vendor_category_2"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_3"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_3"
+ android:description="@string/car_permission_desc_get_car_vendor_category_3"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_3"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_3"
+ android:description="@string/car_permission_desc_set_car_vendor_category_3"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_4"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_4"
+ android:description="@string/car_permission_desc_get_car_vendor_category_4"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_4"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_4"
+ android:description="@string/car_permission_desc_set_car_vendor_category_4"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_5"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_5"
+ android:description="@string/car_permission_desc_get_car_vendor_category_5"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_5"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_5"
+ android:description="@string/car_permission_desc_set_car_vendor_category_5"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_6"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_6"
+ android:description="@string/car_permission_desc_get_car_vendor_category_6"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_6"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_6"
+ android:description="@string/car_permission_desc_set_car_vendor_category_6"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_7"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_7"
+ android:description="@string/car_permission_desc_get_car_vendor_category_7"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_7"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_7"
+ android:description="@string/car_permission_desc_set_car_vendor_category_7"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_8"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_8"
+ android:description="@string/car_permission_desc_get_car_vendor_category_8"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_8"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_8"
+ android:description="@string/car_permission_desc_set_car_vendor_category_8"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_9"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_9"
+ android:description="@string/car_permission_desc_get_car_vendor_category_9"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_9"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_9"
+ android:description="@string/car_permission_desc_set_car_vendor_category_9"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_10"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_10"
+ android:description="@string/car_permission_desc_get_car_vendor_category_10"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_10"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_10"
+ android:description="@string/car_permission_desc_set_car_vendor_category_10"/>
+ <permission android:name="android.car.permission.CAR_MONITOR_INPUT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_monitor_input"
+ android:description="@string/car_permission_desc_monitor_input"/>
+ <permission android:name="android.car.permission.READ_CAR_POWER_POLICY"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_read_car_power_policy"
+ android:description="@string/car_permission_desc_read_car_power_policy"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_POWER_POLICY"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:label="@string/car_permission_label_control_car_power_policy"
+ android:description="@string/car_permission_desc_control_car_power_policy"/>
+ <permission android:name="android.car.permission.CONTROL_SHUTDOWN_PROCESS"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:label="@string/car_permission_label_adjust_shutdown_process"
+ android:description="@string/car_permission_desc_adjust_shutdown_process"/>
+ <permission android:name="android.car.permission.TEMPLATE_RENDERER"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_template_renderer"
+ android:description="@string/car_permission_desc_template_renderer"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_APP_LAUNCH"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_app_launch"
+ android:description="@string/car_permission_desc_control_car_app_launch"/>
+ <permission android:name="android.car.permission.MANAGE_THREAD_PRIORITY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_manage_thread_priority"
+ android:description="@string/car_permission_desc_manage_thread_priority"/>
+ <permission android:name="android.car.permission.BIND_OEM_CAR_SERVICE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_bind_oem_car_service"
+ android:description="@string/car_permission_desc_bind_oem_car_service"/>
+ <permission android:name="android.car.permission.MANAGE_OCCUPANT_ZONE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_manage_occupant_zone"
+ android:description="@string/car_permission_desc_manage_occupant_zone"/>
+ <permission android:name="android.car.permission.CONTROL_STEERING_WHEEL"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_steering_wheel"
+ android:description="@string/car_permission_desc_control_steering_wheel"/>
+ <permission android:name="android.car.permission.USE_REMOTE_ACCESS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_use_remote_access"
+ android:description="@string/car_permission_desc_use_remote_access"/>
+ <permission android:name="android.car.permission.CONTROL_REMOTE_ACCESS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_remote_access"
+ android:description="@string/car_permission_desc_control_remote_access"/>
+ <permission android:name="android.car.permission.READ_ADAS_SETTINGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_adas_settings"
+ android:description="@string/car_permission_desc_read_adas_settings"/>
+ <permission android:name="android.car.permission.CONTROL_ADAS_SETTINGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_adas_settings"
+ android:description="@string/car_permission_desc_control_adas_settings"/>
+ <permission android:name="android.car.permission.READ_ADAS_STATES"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_adas_states"
+ android:description="@string/car_permission_desc_read_adas_states"/>
+ <permission android:name="android.car.permission.CONTROL_ADAS_STATES"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_adas_states"
+ android:description="@string/car_permission_desc_control_adas_states"/>
+ <permission android:name="android.car.permission.ACCESS_MIRRORED_SURFACE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_access_mirrored_surface"
+ android:description="@string/car_permission_desc_access_mirrored_surface"/>
+ <permission android:name="android.car.permission.MIRROR_DISPLAY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_mirror_display"
+ android:description="@string/car_permission_desc_mirror_display"/>
+ <permission android:name="android.car.permission.REGISTER_CAR_SYSTEM_UI_PROXY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_register_car_system_ui_proxy"
+ android:description="@string/car_permission_desc_register_car_system_ui_proxy"/>
+ <permission android:name="android.car.permission.MANAGE_CAR_SYSTEM_UI"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_manage_car_system_ui"
+ android:description="@string/car_permission_desc_manage_car_system_ui"/>
+ <permission android:name="android.car.permission.READ_WINDSHIELD_WIPERS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_windshield_wipers"
+ android:description="@string/car_permission_desc_read_windshield_wipers"/>
+ <permission android:name="android.car.permission.CONTROL_WINDSHIELD_WIPERS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_windshield_wipers"
+ android:description="@string/car_permission_desc_control_windshield_wipers"/>
+ <permission android:name="android.car.permission.MANAGE_DISPLAY_COMPATIBILITY"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_manage_display_compatibility"
+ android:description="@string/car_permission_desc_manage_display_compatibility"/>
+ <permission
+ android:name="android.car.permission.READ_PERSIST_TETHERING_SETTINGS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_persist_tethering_settings"
+ android:description="@string/car_permission_desc_read_persist_tethering_settings" />
+ <permission
+ android:name="android.car.permission.BIND_APP_CARD_PROVIDER"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_bind_app_card_provider"
+ android:description="@string/car_permission_desc_bind_app_card_provider" />
+</manifest>
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/CommandBroadcastReceiver.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/CommandBroadcastReceiver.java
new file mode 100644
index 000000000..02138f36f
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/CommandBroadcastReceiver.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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 andf
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import javax.annotation.Nullable;
+
+public class CommandBroadcastReceiver extends BroadcastReceiver {
+
+ private static @Nullable OnCommandResultListener sOnCommandResultListener;
+
+ public interface OnCommandResultListener {
+ void onCommandResult(@Nullable Intent result);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final OnCommandResultListener listener;
+ synchronized (CommandBroadcastReceiver.class) {
+ listener = sOnCommandResultListener;
+ }
+ if (listener != null) {
+ listener.onCommandResult(intent);
+ }
+ }
+
+ public static void setOnCommandResultListener(@Nullable OnCommandResultListener listener) {
+ synchronized (CommandBroadcastReceiver.class) {
+ sOnCommandResultListener = listener;
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ContactsProviderTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ContactsProviderTest.java
new file mode 100644
index 000000000..f34170a9b
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ContactsProviderTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.permissionpolicy.cts;
+
+import android.content.ContentValues;
+import android.platform.test.annotations.AppModeFull;
+import android.provider.ContactsContract;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify that deprecated contacts permissions are not enforced.
+ */
+@AppModeFull(reason = "Instant apps cannot get the READ_CONTACTS/WRITE_CONTACTS permissions")
+public class ContactsProviderTest extends AndroidTestCase {
+
+ /**
+ * Verifies that query(ContactsContract.Contacts.CONTENT_URI) only requires
+ * permission {@link android.Manifest.permission#READ_CONTACTS}.
+ */
+ @SmallTest
+ public void testQueryContacts() {
+ getContext().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+ null, null, null, null);
+ }
+
+ /**
+ * Verifies that insert(ContactsContract.Contacts.CONTENT_URI) only requires
+ * permission {@link android.Manifest.permission#WRITE_CONTACTS}.
+ */
+ @SmallTest
+ public void testInsertContacts() {
+ try {
+ getContext().getContentResolver().insert(ContactsContract.Contacts.CONTENT_URI,
+ new ContentValues());
+ } catch (SecurityException e) {
+ fail("insert(ContactsContract.Contacts.CONTENT_URI) threw SecurityException");
+ } catch (UnsupportedOperationException e) {
+ // It is okay for this fail in this manner.
+ }
+ }
+
+ /**
+ * Verifies that query(ContactsContract.Profile.CONTENT_URI) only requires
+ * permission {@link android.Manifest.permission#READ_CONTACTS}.
+ */
+ @SmallTest
+ public void testQueryProfile() {
+ getContext().getContentResolver().query(ContactsContract.Profile.CONTENT_URI,
+ null, null, null, null);
+ }
+
+ /**
+ * Verifies that insert(ContactsContract.Profile.CONTENT_URI) only requires
+ * permission {@link android.Manifest.permission#WRITE_CONTACTS}. The provider won't
+ * actually let us execute this. But at least it shouldn't throw a security exception.
+ */
+ @SmallTest
+ public void testInsertProfile() {
+ try {
+ getContext().getContentResolver().insert(ContactsContract.Profile.CONTENT_URI,
+ new ContentValues(0));
+ } catch (SecurityException e) {
+ fail("insert(ContactsContract.Profile.CONTENT_URI) threw SecurityException");
+ } catch (UnsupportedOperationException e) {
+ // It is okay for this fail in this manner.
+ }
+ }
+
+ /**
+ * Verifies that update(ContactsContract.Profile.CONTENT_URI) only requires
+ * permission {@link android.Manifest.permission#WRITE_CONTACTS}.
+ */
+ @SmallTest
+ public void testUpdateProfile() {
+ getContext().getContentResolver().update(ContactsContract.Profile.CONTENT_URI,
+ new ContentValues(0), null, null);
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/IntelligenceRolesPolicyTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/IntelligenceRolesPolicyTest.java
new file mode 100644
index 000000000..e2cfe86ca
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/IntelligenceRolesPolicyTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.R;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@AppModeFull(reason = "Instant apps cannot read the system servers permission")
+@RunWith(Parameterized.class)
+public class IntelligenceRolesPolicyTest {
+ private final int mConfigKey;
+
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection intelligenceRoles() {
+ return Arrays.asList(new Object[][]{
+ {"systemUiIntelligence", R.string.config_systemUiIntelligence},
+ {"systemAmbientAudioIntelligence", R.string.config_systemAmbientAudioIntelligence},
+ {"systemAudioIntelligence", R.string.config_systemAudioIntelligence},
+ {"systemNotificationIntelligence", R.string.config_systemNotificationIntelligence},
+ {"systemTextIntelligence", R.string.config_systemTextIntelligence},
+ {"systemVisualIntelligence", R.string.config_systemVisualIntelligence},
+ });
+ }
+
+ public IntelligenceRolesPolicyTest(String unusedName, int configKey) {
+ mConfigKey = configKey;
+ }
+
+ @Test
+ public void testNoInternetPermissionRequested() throws Exception {
+ assumeTrue(ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S));
+
+ String packageName = sContext.getResources().getString(mConfigKey);
+ assumeTrue(!Strings.isNullOrEmpty(packageName));
+
+ List<String> requestedPermissions;
+
+ try {
+ requestedPermissions = getRequestedPermissions(sContext, packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ // A package is not found, despite overlay config pointing to it. Strictly speaking that
+ // means that the policy for being an intelligence role is fulfilled.
+ return;
+ }
+
+ assertWithMessage("Package " + packageName + "MUST NOT request INTERNET permission. "
+ + "Instead packages MUST access the internet through well-defined APIs in an open "
+ + "source project.")
+ .that(requestedPermissions)
+ .doesNotContain(android.Manifest.permission.INTERNET);
+ }
+
+ private static List<String> getRequestedPermissions(Context context, String pkg)
+ throws PackageManager.NameNotFoundException {
+ PackageInfo packageInfo = context.getPackageManager()
+ .getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
+
+ return Arrays.asList(packageInfo.requestedPermissions);
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoCaptureAudioOutputPermissionTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoCaptureAudioOutputPermissionTest.java
new file mode 100644
index 000000000..ef38573ab
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoCaptureAudioOutputPermissionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import android.content.pm.PackageManager;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder.AudioSource;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify the capture system video output permission requirements.
+ */
+public class NoCaptureAudioOutputPermissionTest extends AndroidTestCase {
+ /**
+ * Verify that the AudioRecord constructor fails to create a recording object
+ * when the app does not have permission to capture audio output.
+ * For the purposes of this test, the app must already have the normal audio
+ * record permission, just not the capture audio output permission.
+ * <p>Requires permission:
+ * {@link android.Manifest.permission#RECORD_AUDIO} and
+ * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT}.
+ */
+ @SmallTest
+ public void testCreateAudioRecord() {
+ int bufferSize = AudioRecord.getMinBufferSize(44100,
+ AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
+
+ if (bufferSize <= 0)
+ {
+ // getMinBufferSize() returns an invalid buffer size.
+ // That could be because there is no microphone. In that case,
+ // use this buffer size to test AudioRecord creation.
+ PackageManager packageManager = mContext.getPackageManager();
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ bufferSize = 44100;
+ }
+ }
+
+ // The attempt to create the AudioRecord object succeeds even if the
+ // app does not have permission, but the object is not usable.
+ // The API should probably throw SecurityException but it was not originally
+ // designed to do that and it's not clear we can change it now.
+ AudioRecord record = new AudioRecord(AudioSource.REMOTE_SUBMIX, 44100,
+ AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
+ try {
+ assertTrue("AudioRecord state should not be INITIALIZED because the application"
+ + "does not have permission to access the remote submix source",
+ record.getState() != AudioRecord.STATE_INITIALIZED);
+ } finally {
+ record.release();
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoProcessOutgoingCallPermissionTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoProcessOutgoingCallPermissionTest.java
new file mode 100644
index 000000000..ae2bed3fb
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoProcessOutgoingCallPermissionTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.platform.test.annotations.AppModeFull;
+import android.telecom.TelecomManager;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Verify that processing outgoing calls requires Permission.
+ */
+@AppModeFull(reason = "Instant apps cannot hold PROCESS_OUTGOING_CALL")
+public class NoProcessOutgoingCallPermissionTest {
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ // Time to wait for call to be placed.
+ private static final int CALL_START_WAIT_TIME_SEC = 30;
+ // Time to wait for a broadcast to be received after we verify that the test app which has
+ // the proper permission got the broadcast
+ private static final int POST_CALL_START_WAIT_TIME_SEC = 5;
+
+ private static final String APK_INSTALL_LOCATION =
+ "/data/local/tmp/cts-permissionpolicy/CtsProcessOutgoingCalls.apk";
+ private static final String LOG_TAG = "NoProcessOutgoingCallPermissionTest";
+
+ private static final String ACTION_TEST_APP_RECEIVED_CALL =
+ "android.permissionpolicy.cts.TEST_APP_RECEIVED_CALL";
+ private static final String TEST_PKG_NAME = "android.permissionpolicy.cts.receivecallbroadcast";
+
+ private final CountDownLatch mTestAppBroadcastLatch = new CountDownLatch(1);
+ private final CountDownLatch mSystemBroadcastLatch = new CountDownLatch(1);
+
+ private void callPhone() {
+ Uri uri = Uri.parse("tel:123456");
+ Intent intent = new Intent(Intent.ACTION_CALL, uri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ Log.i(LOG_TAG, "Called phone: " + uri.toString());
+ }
+
+ /**
+ * Verify that to process an outgoing call requires Permission.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#PROCESS_OUTGOING_CALLS}
+ */
+ // TODO: add back to LargeTest when test can cancel initiated call
+ @Test
+ public void testProcessOutgoingCall() throws InterruptedException {
+ final PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING) ||
+ !pm.hasSystemFeature(PackageManager.FEATURE_SIP_VOIP)) {
+ return;
+ }
+
+ OutgoingCallBroadcastReceiver rcvr = new OutgoingCallBroadcastReceiver();
+ IntentFilter filter = new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL);
+ filter.addAction(ACTION_TEST_APP_RECEIVED_CALL);
+ mContext.registerReceiver(rcvr, filter, Context.RECEIVER_EXPORTED);
+ // start the test app, so that it can receive the broadcast
+ mContext.startActivity(new Intent().setComponent(new ComponentName(TEST_PKG_NAME,
+ TEST_PKG_NAME + ".ProcessOutgoingCallReceiver$BaseActivity"))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+
+ callPhone();
+
+ boolean testAppGotBroadcast =
+ mTestAppBroadcastLatch.await(CALL_START_WAIT_TIME_SEC, TimeUnit.SECONDS);
+ Assert.assertTrue("Expected test app to receive broadcast within "
+ + CALL_START_WAIT_TIME_SEC + " seconds", testAppGotBroadcast);
+ boolean testClassGotBroadcast =
+ mSystemBroadcastLatch.await(POST_CALL_START_WAIT_TIME_SEC, TimeUnit.SECONDS);
+ Assert.assertFalse("Outgoing call processed without proper permissions",
+ testClassGotBroadcast);
+ }
+
+ @Before
+ public void installApp() {
+ String installResult = runShellCommandOrThrow("pm install -g " + APK_INSTALL_LOCATION);
+ assertThat(installResult.trim()).isEqualTo("Success");
+ }
+
+ @After
+ public void endCall() {
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ mContext.getSystemService(TelecomManager.class).endCall();
+ });
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
+
+ public class OutgoingCallBroadcastReceiver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_TEST_APP_RECEIVED_CALL.equals(intent.getAction())) {
+ mTestAppBroadcastLatch.countDown();
+ return;
+ }
+ Bundle xtrs = intent.getExtras();
+ Log.e(LOG_TAG, xtrs.toString());
+ mSystemBroadcastLatch.countDown();
+ }
+ }
+
+}
+
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoReceiveSmsPermissionTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoReceiveSmsPermissionTest.java
new file mode 100644
index 000000000..c02d98077
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoReceiveSmsPermissionTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SystemUserOnly;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Verify Sms and Mms cannot be received without required permissions.
+ * Uses {@link android.telephony.SmsManager}.
+ */
+@AppModeFull(reason = "Instant apps cannot get the SEND_SMS permission")
+@SystemUserOnly(reason = "Secondary users have the DISALLOW_SMS user restriction")
+public class NoReceiveSmsPermissionTest extends AndroidTestCase {
+
+ private static final int SMS_DELIVERED_WAIT_TIME_MILLIS = 4000;
+ private static final String TELEPHONY_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
+ private static final String MESSAGE_STATUS_RECEIVED_ACTION =
+ "com.android.cts.permission.sms.MESSAGE_STATUS_RECEIVED_ACTION";
+ private static final String MESSAGE_SENT_ACTION =
+ "com.android.cts.permission.sms.MESSAGE_SENT";
+ private static final String APP_SPECIFIC_SMS_RECEIVED_ACTION =
+ "com.android.cts.permission.sms.APP_SPECIFIC_SMS_RECEIVED";
+
+
+ private static final String LOG_TAG = "NoReceiveSmsPermissionTest";
+
+ // List of carrier-id that does not support loop back messages
+ // This is copied from
+ // cts/tests/tests/telephony/current/src/android/telephony/cts/CarrierCapability.java
+ public static final List<Integer> UNSUPPORT_LOOP_BACK_MESSAGES =
+ Arrays.asList(
+ 1 // "T-Mobile - US"
+ );
+
+ private Semaphore mSemaphore = new Semaphore(0);
+
+ /**
+ * Verify that SmsManager.sendTextMessage requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#SEND_SMS}.
+ *
+ * Note: this test requires that the device under test reports a valid phone number
+ */
+ public void testReceiveTextMessage() {
+ PackageManager packageManager = mContext.getPackageManager();
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ return;
+ }
+
+ // register our test receiver to receive SMSs. This won't throw a SecurityException,
+ // so test needs to wait to determine if it actual receives an SMS
+ // admittedly, this is a weak verification
+ // this test should be used in conjunction with a test that verifies an SMS can be
+ // received successfully using the same logic if all permissions are in place
+ IllegalSmsReceiver receiver = new IllegalSmsReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TELEPHONY_SMS_RECEIVED);
+ filter.addAction(MESSAGE_SENT_ACTION);
+ filter.addAction(MESSAGE_STATUS_RECEIVED_ACTION);
+
+ getContext().registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
+ sendSMSToSelf("test");
+
+ waitForForEvents(mSemaphore, 1);
+ assertTrue("[RERUN] Sms not sent successfully. Check signal.",
+ receiver.isMessageSent());
+ assertFalse("Sms received without proper permissions", receiver.isSmsReceived());
+ }
+
+ /**
+ * Verify that without {@link android.Manifest.permission#RECEIVE_SMS} that an SMS sent
+ * containing a nonce from {@link SmsManager#createAppSpecificSmsToken} is delivered
+ * to the app.
+ */
+ public void testAppSpecificSmsToken() {
+ PackageManager packageManager = mContext.getPackageManager();
+ if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ return;
+ }
+
+ int carrierId = getContext().getSystemService(TelephonyManager.class).getSimCarrierId();
+ assertFalse("[RERUN] Carrier [carrier-id: " + carrierId + "] does not support "
+ + "loop back messages. Use another carrier.",
+ UNSUPPORT_LOOP_BACK_MESSAGES.contains(carrierId));
+
+ AppSpecificSmsReceiver receiver = new AppSpecificSmsReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(TELEPHONY_SMS_RECEIVED);
+ filter.addAction(MESSAGE_SENT_ACTION);
+ filter.addAction(MESSAGE_STATUS_RECEIVED_ACTION);
+ filter.addAction(APP_SPECIFIC_SMS_RECEIVED_ACTION);
+ getContext().registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
+
+ PendingIntent receivedIntent = PendingIntent.getBroadcast(getContext(), 0,
+ new Intent(APP_SPECIFIC_SMS_RECEIVED_ACTION)
+ .setPackage(getContext().getPackageName()),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
+
+ String token = SmsManager.getDefault().createAppSpecificSmsToken(receivedIntent);
+ String message = "test message, token=" + token;
+ sendSMSToSelf(message);
+
+ waitForForEvents(mSemaphore, 1);
+ assertTrue("[RERUN] Sms not sent successfully. Check signal.",
+ receiver.isMessageSent());
+ assertFalse("Sms received without proper permissions", receiver.isSmsReceived());
+ waitForForEvents(mSemaphore, 1);
+ assertTrue("App specific SMS intent not triggered", receiver.isAppSpecificSmsReceived());
+ }
+
+ private boolean waitForForEvents(Semaphore semaphore, int expectedNumberOfEvents) {
+ for (int i = 0; i < expectedNumberOfEvents; i++) {
+ try {
+ if (!semaphore.tryAcquire(SMS_DELIVERED_WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void sendSMSToSelf(String message) {
+ PendingIntent sentIntent = PendingIntent.getBroadcast(getContext(), 0,
+ new Intent(MESSAGE_SENT_ACTION).setPackage(getContext().getPackageName()),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
+ PendingIntent deliveryIntent = PendingIntent.getBroadcast(getContext(), 0,
+ new Intent(MESSAGE_STATUS_RECEIVED_ACTION)
+ .setPackage(getContext().getPackageName()),
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
+
+ SubscriptionManager subscription = (SubscriptionManager)
+ getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int subscriptionId = subscription.getActiveDataSubscriptionId();
+
+ assertFalse("[RERUN] No active telephony subscription. Check there is one enabled.",
+ subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+ // get current phone number
+ String currentNumber = subscription.getPhoneNumber(subscriptionId);
+
+ // fallback to getActiveSubscriptionInfo if number is empty
+ if (TextUtils.isEmpty(currentNumber)) {
+ SubscriptionInfo subInfo = subscription.getActiveSubscriptionInfo(subscriptionId);
+
+ assertTrue("[RERUN] No info for the active telephony subscription.",
+ subInfo != null);
+ currentNumber = subInfo.getNumber();
+ }
+ assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
+ TextUtils.isEmpty(currentNumber));
+
+ Log.i(LOG_TAG, String.format("Sending SMS to self: %s", currentNumber));
+ sendSms(currentNumber, message, sentIntent, deliveryIntent);
+ }
+
+ protected void sendSms(String currentNumber, String text, PendingIntent sentIntent,
+ PendingIntent deliveryIntent) {
+ SmsManager.getDefault().sendTextMessage(currentNumber, null, text, sentIntent,
+ deliveryIntent);
+ }
+
+ /**
+ * A receiver that tracks if message was sent and received
+ */
+ public class IllegalSmsReceiver extends BroadcastReceiver {
+
+ private boolean mIsSmsReceived = false;
+ private boolean mIsMessageSent = false;
+
+ public void onReceive(Context context, Intent intent) {
+ if (TELEPHONY_SMS_RECEIVED.equals(intent.getAction())) {
+ // this is bad, received sms without having SMS permission
+ setSmsReceived();
+ } else if (MESSAGE_STATUS_RECEIVED_ACTION.equals(intent.getAction())) {
+ handleResultCode(getResultCode(), "delivery");
+ } else if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
+ handleResultCode(getResultCode(), "sent");
+ } else {
+ Log.w(LOG_TAG, String.format("unknown intent received: %s", intent.getAction()));
+ }
+
+ }
+
+ public boolean isSmsReceived() {
+ return mIsSmsReceived;
+ }
+
+ private synchronized void setSmsReceived() {
+ mIsSmsReceived = true;
+ notify();
+ }
+
+ public boolean isMessageSent() {
+ return mIsMessageSent;
+ }
+
+ private void handleResultCode(int resultCode, String action) {
+ if (resultCode == Activity.RESULT_OK) {
+ Log.i(LOG_TAG, String.format("message %1$s successful", action));
+ setMessageSentSuccess();
+ } else {
+ setMessageSentFailure();
+ String reason = getErrorReason(resultCode);
+ Log.e(LOG_TAG, String.format("message %1$s failed: %2$s", action, reason));
+ }
+ }
+
+ private synchronized void setMessageSentSuccess() {
+ mIsMessageSent = true;
+ // set this to true, but don't notify receiver since we don't know if message received
+ // yet
+ }
+
+ private synchronized void setMessageSentFailure() {
+ mIsMessageSent = false;
+ // test environment failure, notify observer so it can stop listening
+ // TODO: should test retry?
+ notify();
+ }
+
+ private String getErrorReason(int resultCode) {
+ switch (resultCode) {
+ case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+ return "generic failure";
+ case SmsManager.RESULT_ERROR_NO_SERVICE:
+ return "no service";
+ case SmsManager.RESULT_ERROR_NULL_PDU:
+ return "null pdu";
+ case SmsManager.RESULT_ERROR_RADIO_OFF:
+ return "Radio off";
+ }
+ return "unknown";
+ }
+ }
+
+ public class AppSpecificSmsReceiver extends IllegalSmsReceiver {
+ private boolean mAppSpecificSmsReceived = false;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (APP_SPECIFIC_SMS_RECEIVED_ACTION.equals(intent.getAction())) {
+ mAppSpecificSmsReceived = true;
+ } else {
+ super.onReceive(context, intent);
+ }
+ try {
+ mSemaphore.release();
+ } catch (Exception ex) {
+ Log.e(LOG_TAG, "mSemaphore: Got exception in releasing semaphore, ex=" + ex);
+ }
+ }
+
+ public boolean isAppSpecificSmsReceived() {
+ return mAppSpecificSmsReceived;
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoWriteSecureSettingsPermissionTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoWriteSecureSettingsPermissionTest.java
new file mode 100644
index 000000000..2e4a806b6
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/NoWriteSecureSettingsPermissionTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import android.Manifest;
+import android.content.ContentValues;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+
+/**
+ * Verify secure settings cannot be written to without required permissions.
+ */
+public class NoWriteSecureSettingsPermissionTest extends AndroidTestCase {
+
+ /**
+ * Verify that write to secure settings requires permissions.
+ * This test app must have WRITE_SETTINGS permission but not WRITE_SECURE_SETTINGS
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
+ */
+ public void testWriteSecureSettings() {
+ try {
+ ContentValues values = new ContentValues();
+ values.put(Settings.Secure.NAME, Settings.Secure.ACCESSIBILITY_ENABLED);
+ values.put(Settings.Secure.VALUE, Boolean.TRUE);
+ getContext().getContentResolver().insert(Settings.Secure.CONTENT_URI, values);
+ fail("expected SecurityException requiring "
+ + Manifest.permission.WRITE_SECURE_SETTINGS);
+ } catch (SecurityException expected) {
+ /* do nothing */
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionMaxSdkVersionTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionMaxSdkVersionTest.java
new file mode 100644
index 000000000..8bf3e83a4
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionMaxSdkVersionTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+
+/**
+ * Verify permission behaviors with android:maxSdkVersion
+ */
+public class PermissionMaxSdkVersionTest extends AndroidTestCase {
+ // These two permission names must match the corresponding <uses-permission>
+ // declarations in the test app manifest.
+ static final String UNGRANTABLE_PERMISSION = "android.permission.INTERNET";
+ static final String GRANTABLE_PERMISSION = "android.permission.ACCESS_NETWORK_STATE";
+
+ /**
+ * Verify that with android:maxSdkVersion set to a previous API level,
+ * the permission is not being granted.
+ */
+ @SmallTest
+ public void testMaxSdkInPast() {
+ int result = mContext.checkPermission(UNGRANTABLE_PERMISSION,
+ Process.myPid(), Process.myUid());
+ assertEquals("Permissions with maxSdkVersion in the past should not be granted",
+ result,
+ PackageManager.PERMISSION_DENIED);
+ }
+
+ /**
+ * Verify that with android:maxSdkVersion set to a future API level,
+ * the permission is being granted.
+ */
+ @SmallTest
+ public void testMaxSdkInFuture() {
+ int result = mContext.checkPermission(GRANTABLE_PERMISSION,
+ Process.myPid(), Process.myUid());
+ assertEquals("Permissions with maxSdkVersion in the future should be granted",
+ result,
+ PackageManager.PERMISSION_GRANTED);
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java
new file mode 100644
index 000000000..18c214a1f
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PermissionPolicyTest.java
@@ -0,0 +1,551 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.permissionpolicy.cts;
+
+import static android.content.pm.PermissionInfo.FLAG_INSTALLED;
+import static android.content.pm.PermissionInfo.PROTECTION_MASK_BASE;
+import static android.os.Build.VERSION.SECURITY_PATCH;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Tests for permission policy on the platform.
+ */
+@AppModeFull(reason = "Instant apps cannot read the system servers permission")
+@RunWith(AndroidJUnit4.class)
+public class PermissionPolicyTest {
+ private static final Date HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE = parseDate("2017-11-01");
+ private static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION
+ = "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
+
+ private static final Date MANAGE_COMPANION_DEVICES_PATCH_DATE = parseDate("2020-07-01");
+ private static final String MANAGE_COMPANION_DEVICES_PERMISSION
+ = "android.permission.MANAGE_COMPANION_DEVICES";
+
+ private static final String LOG_TAG = "PermissionProtectionTest";
+
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+
+ private static final String PLATFORM_ROOT_NAMESPACE = "android.";
+
+ private static final String TAG_PERMISSION = "permission";
+ private static final String TAG_PERMISSION_GROUP = "permission-group";
+
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_PERMISSION_GROUP = "permissionGroup";
+ private static final String ATTR_PERMISSION_FLAGS = "permissionFlags";
+ private static final String ATTR_PROTECTION_LEVEL = "protectionLevel";
+ private static final String ATTR_BACKGROUND_PERMISSION = "backgroundPermission";
+ private static final String ATTR_FEATURE_FLAG = "featureFlag";
+
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ @Test
+ public void shellIsOnlySystemAppThatRequestsRevokePostNotificationsWithoutKill() {
+ List<PackageInfo> pkgs = sContext.getPackageManager().getInstalledPackages(
+ PackageManager.PackageInfoFlags.of(
+ PackageManager.GET_PERMISSIONS | PackageManager.MATCH_ALL));
+ int shellUid = Process.myUserHandle().getUid(Process.SHELL_UID);
+ for (PackageInfo pkg : pkgs) {
+ Assert.assertFalse(pkg.applicationInfo.uid != shellUid
+ && hasRevokeNotificationNoKillPermission(pkg));
+ }
+ }
+
+ @Test
+ public void platformPermissionPolicyIsUnaltered() throws Exception {
+ Map<String, PermissionInfo> declaredPermissionsMap =
+ getPermissionsForPackage(sContext, PLATFORM_PACKAGE_NAME);
+
+ List<String> offendingList = new ArrayList<>();
+
+ List<PermissionGroupInfo> declaredGroups = sContext.getPackageManager()
+ .getAllPermissionGroups(0);
+ Set<String> declaredGroupsSet = new ArraySet<>();
+ for (PermissionGroupInfo declaredGroup : declaredGroups) {
+ declaredGroupsSet.add(declaredGroup.name);
+ }
+
+ Set<String> expectedPermissionGroups = loadExpectedPermissionGroupNames(
+ R.raw.android_manifest);
+ List<ExpectedPermissionInfo> expectedPermissions = loadExpectedPermissions(
+ R.raw.android_manifest);
+
+ if (sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ expectedPermissions.addAll(loadExpectedPermissions(R.raw.automotive_android_manifest));
+ String carServicePackageName = SystemProperties.get("ro.android.car.carservice.package",
+ null);
+
+ assertWithMessage("Car service package not defined").that(
+ carServicePackageName).isNotNull();
+
+ declaredPermissionsMap.putAll(
+ getPermissionsForPackage(sContext, carServicePackageName));
+
+ // Load signature permission declared in CarService-builtin
+ String carServiceBuiltInPackageName = "com.android.car";
+ Map<String, PermissionInfo> carServiceBuiltInPermissionsMap = getPermissionsForPackage(
+ sContext, carServiceBuiltInPackageName);
+ // carServiceBuiltInPermissionsMap should only have signature permissions and those
+ // permissions should not be defined in car service updatable.
+ for (Map.Entry<String, PermissionInfo> permissionData : carServiceBuiltInPermissionsMap
+ .entrySet()) {
+ PermissionInfo carServiceBuiltInDeclaredPermission = permissionData.getValue();
+ String carServiceBuiltInDeclaredPermissionName = permissionData.getKey();
+
+ // Signature only permission should be defined in built-in car service
+ if ((carServiceBuiltInDeclaredPermission
+ .getProtection() != PermissionInfo.PROTECTION_SIGNATURE)
+ || (carServiceBuiltInDeclaredPermission.getProtectionFlags() != 0)) {
+ offendingList.add("Permission " + carServiceBuiltInDeclaredPermissionName
+ + " should be signature only permission to be declared in"
+ + " carServiceBuiltIn package.");
+ continue;
+ }
+
+ if (declaredPermissionsMap.get(carServiceBuiltInDeclaredPermissionName) != null) {
+ offendingList.add("Permission " + carServiceBuiltInDeclaredPermissionName
+ + " from car service builtin is already declared in other packages.");
+ continue;
+ }
+ }
+ declaredPermissionsMap.putAll(carServiceBuiltInPermissionsMap);
+ }
+
+ for (ExpectedPermissionInfo expectedPermission : expectedPermissions) {
+ String expectedPermissionName = expectedPermission.name;
+ if (shouldSkipPermission(expectedPermissionName)) {
+ // This permission doesn't need to exist yet, but will exist in
+ // a future SPL. It is acceptable to declare the permission
+ // even in an earlier SPL, so we remove it here so it doesn't
+ // trigger a failure after the loop.
+ declaredPermissionsMap.remove(expectedPermissionName);
+ continue;
+ }
+
+ // OEMs cannot remove permissions
+ PermissionInfo declaredPermission = declaredPermissionsMap.get(expectedPermissionName);
+ if (declaredPermission == null) {
+ offendingList.add("Permission " + expectedPermissionName + " must be declared");
+ continue;
+ }
+
+ // We want to end up with OEM defined permissions and groups to check their namespace
+ declaredPermissionsMap.remove(expectedPermissionName);
+
+ // OEMs cannot change permission protection
+ final int expectedProtection = expectedPermission.protectionLevel
+ & PROTECTION_MASK_BASE;
+ final int declaredProtection = declaredPermission.protectionLevel
+ & PROTECTION_MASK_BASE;
+ if (expectedProtection != declaredProtection) {
+ offendingList.add(
+ String.format(
+ "Permission %s invalid protection level %x, expected %x",
+ expectedPermissionName, declaredProtection, expectedProtection));
+ }
+
+ // OEMs cannot change permission flags
+ final int expectedFlags = expectedPermission.flags;
+ final int declaredFlags = (declaredPermission.flags & ~FLAG_INSTALLED);
+ if (expectedFlags != declaredFlags) {
+ offendingList.add(
+ String.format(
+ "Permission %s invalid flags %x, expected %x",
+ expectedPermissionName,
+ declaredFlags,
+ expectedFlags));
+ }
+
+ // OEMs cannot change permission protection flags
+ final int expectedProtectionFlags =
+ expectedPermission.protectionLevel & ~PROTECTION_MASK_BASE;
+ final int declaredProtectionFlags = declaredPermission.getProtectionFlags();
+ if (expectedProtectionFlags != declaredProtectionFlags) {
+ offendingList.add(
+ String.format(
+ "Permission %s invalid enforced protection %x, expected %x",
+ expectedPermissionName,
+ declaredProtectionFlags,
+ expectedProtectionFlags));
+ }
+
+ // OEMs cannot change permission grouping
+ if ((declaredPermission.protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+ if (!Objects.equals(expectedPermission.group, declaredPermission.group)) {
+ offendingList.add(
+ "Permission " + expectedPermissionName + " not in correct group "
+ + "(expected=" + expectedPermission.group + " actual="
+ + declaredPermission.group);
+ }
+
+ if (declaredPermission.group != null
+ && !declaredGroupsSet.contains(declaredPermission.group)) {
+ offendingList.add(
+ "Permission group " + expectedPermission.group + " must be defined");
+ }
+ }
+
+ // OEMs cannot change background permission mapping
+ if (!Objects.equals(expectedPermission.backgroundPermission,
+ declaredPermission.backgroundPermission)) {
+ offendingList.add(
+ String.format(
+ "Permission %s invalid background permission %s, expected %s",
+ expectedPermissionName,
+ declaredPermission.backgroundPermission,
+ expectedPermission.backgroundPermission));
+ }
+ }
+
+ // OEMs cannot define permissions in the platform namespace
+ for (String permission : declaredPermissionsMap.keySet()) {
+ if (permission.startsWith(PLATFORM_ROOT_NAMESPACE)) {
+ final PermissionInfo permInfo = declaredPermissionsMap.get(permission);
+ offendingList.add(
+ "Cannot define permission " + permission
+ + ", package " + permInfo.packageName
+ + " in android namespace");
+ }
+ }
+
+ // OEMs cannot define groups in the platform namespace
+ for (PermissionGroupInfo declaredGroup : declaredGroups) {
+ if (!expectedPermissionGroups.contains(declaredGroup.name)) {
+ if (declaredGroup.name != null) {
+ if (declaredGroup.packageName.equals(PLATFORM_PACKAGE_NAME)
+ && declaredGroup.name.startsWith(PLATFORM_ROOT_NAMESPACE)) {
+ offendingList.add(
+ "Cannot define group " + declaredGroup.name
+ + ", package " + declaredGroup.packageName
+ + " in android namespace");
+ }
+ }
+ }
+ }
+
+ // OEMs cannot define new ephemeral permissions
+ for (String permission : declaredPermissionsMap.keySet()) {
+ PermissionInfo info = declaredPermissionsMap.get(permission);
+ if ((info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
+ offendingList.add("Cannot define new instant permission " + permission);
+ }
+ }
+
+ // Fail on any offending item
+ assertWithMessage("list of offending permissions").that(offendingList).isEmpty();
+ }
+
+ private boolean hasRevokeNotificationNoKillPermission(PackageInfo info) {
+ if (info.requestedPermissions == null) {
+ return false;
+ }
+
+ for (int i = 0; i < info.requestedPermissions.length; i++) {
+ if (Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL.equals(
+ info.requestedPermissions[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List<ExpectedPermissionInfo> loadExpectedPermissions(int resourceId) throws Exception {
+ List<ExpectedPermissionInfo> permissions = new ArrayList<>();
+ DeviceFlagsValueProvider flagsValueProvider = new DeviceFlagsValueProvider();
+ flagsValueProvider.setUp();
+ try (InputStream in = sContext.getResources().openRawResource(resourceId)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_PERMISSION.equals(parser.getName())) {
+ String featureFlag = parser.getAttributeValue(null, ATTR_FEATURE_FLAG);
+ if (featureFlag != null) {
+ featureFlag = featureFlag.trim();
+ boolean invert = featureFlag.startsWith("!");
+ if (invert) {
+ featureFlag = featureFlag.substring(1).trim();
+ }
+ boolean flagEnabled =
+ invert != flagsValueProvider.getBoolean(featureFlag);
+ if (!flagEnabled) {
+ continue;
+ }
+ }
+
+ ExpectedPermissionInfo permissionInfo = new ExpectedPermissionInfo(
+ parser.getAttributeValue(null, ATTR_NAME),
+ parser.getAttributeValue(null, ATTR_PERMISSION_GROUP),
+ parser.getAttributeValue(null, ATTR_BACKGROUND_PERMISSION),
+ parsePermissionFlags(
+ parser.getAttributeValue(null, ATTR_PERMISSION_FLAGS)),
+ parseProtectionLevel(
+ parser.getAttributeValue(null, ATTR_PROTECTION_LEVEL)));
+ permissions.add(permissionInfo);
+ } else {
+ Log.e(LOG_TAG, "Unknown tag " + parser.getName());
+ }
+ }
+ } finally {
+ flagsValueProvider.tearDownBeforeTest();
+ }
+
+ return permissions;
+ }
+
+ private Set<String> loadExpectedPermissionGroupNames(int resourceId) throws Exception {
+ ArraySet<String> permissionGroups = new ArraySet<>();
+ try (InputStream in = sContext.getResources().openRawResource(resourceId)) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+
+ final int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_PERMISSION_GROUP.equals(parser.getName())) {
+ permissionGroups.add(parser.getAttributeValue(null, ATTR_NAME));
+ } else {
+ Log.e(LOG_TAG, "Unknown tag " + parser.getName());
+ }
+ }
+ }
+ return permissionGroups;
+ }
+
+ private static int parsePermissionFlags(@Nullable String permissionFlagsString) {
+ if (permissionFlagsString == null) {
+ return 0;
+ }
+
+ int protectionFlags = 0;
+ String[] fragments = permissionFlagsString.split("\\|");
+ for (String fragment : fragments) {
+ switch (fragment.trim()) {
+ case "removed": {
+ protectionFlags |= PermissionInfo.FLAG_REMOVED;
+ } break;
+ case "costsMoney": {
+ protectionFlags |= PermissionInfo.FLAG_COSTS_MONEY;
+ } break;
+ case "hardRestricted": {
+ protectionFlags |= PermissionInfo.FLAG_HARD_RESTRICTED;
+ } break;
+ case "immutablyRestricted": {
+ protectionFlags |= PermissionInfo.FLAG_IMMUTABLY_RESTRICTED;
+ } break;
+ case "softRestricted": {
+ protectionFlags |= PermissionInfo.FLAG_SOFT_RESTRICTED;
+ } break;
+ }
+ }
+ return protectionFlags;
+ }
+
+ private static int parseProtectionLevel(String protectionLevelString) {
+ int protectionLevel = 0;
+ String[] fragments = protectionLevelString.split("\\|");
+ for (String fragment : fragments) {
+ switch (fragment.trim()) {
+ case "normal": {
+ protectionLevel |= PermissionInfo.PROTECTION_NORMAL;
+ } break;
+ case "dangerous": {
+ protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+ } break;
+ case "signature": {
+ protectionLevel |= PermissionInfo.PROTECTION_SIGNATURE;
+ } break;
+ case "signatureOrSystem": {
+ protectionLevel |= PermissionInfo.PROTECTION_SIGNATURE;
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_SYSTEM;
+ } break;
+ case "internal": {
+ protectionLevel |= PermissionInfo.PROTECTION_INTERNAL;
+ } break;
+ case "system": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_SYSTEM;
+ } break;
+ case "installer": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_INSTALLER;
+ } break;
+ case "verifier": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_VERIFIER;
+ } break;
+ case "preinstalled": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_PREINSTALLED;
+ } break;
+ case "pre23": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_PRE23;
+ } break;
+ case "appop": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_APPOP;
+ } break;
+ case "development": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_DEVELOPMENT;
+ } break;
+ case "privileged": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_PRIVILEGED;
+ } break;
+ case "oem": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_OEM;
+ } break;
+ case "vendorPrivileged": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED;
+ } break;
+ case "setup": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_SETUP;
+ } break;
+ case "textClassifier": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER;
+ } break;
+ case "configurator": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_CONFIGURATOR;
+ } break;
+ case "incidentReportApprover": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER;
+ } break;
+ case "appPredictor": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR;
+ } break;
+ case "instant": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_INSTANT;
+ } break;
+ case "runtime": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY;
+ } break;
+ case "companion": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_COMPANION;
+ } break;
+ case "retailDemo": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO;
+ } break;
+ case "recents": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_RECENTS;
+ } break;
+ case "role": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_ROLE;
+ } break;
+ case "knownSigner": {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER;
+ } break;
+ case "module" : {
+ protectionLevel |= PermissionInfo.PROTECTION_FLAG_MODULE;
+ } break;
+ }
+ }
+ return protectionLevel;
+ }
+
+ private static Map<String, PermissionInfo> getPermissionsForPackage(Context context, String pkg)
+ throws NameNotFoundException {
+ PackageInfo packageInfo = context.getPackageManager()
+ .getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
+ Map<String, PermissionInfo> declaredPermissionsMap = new ArrayMap<>();
+
+ for (PermissionInfo declaredPermission : packageInfo.permissions) {
+ declaredPermissionsMap.put(declaredPermission.name, declaredPermission);
+ }
+ return declaredPermissionsMap;
+ }
+
+ private static Date parseDate(String date) {
+ Date patchDate = new Date();
+ try {
+ SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd");
+ patchDate = template.parse(date);
+ } catch (ParseException e) {
+ }
+
+ return patchDate;
+ }
+
+ private boolean shouldSkipPermission(String permissionName) {
+ switch (permissionName) {
+ case HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION:
+ return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE);
+ case MANAGE_COMPANION_DEVICES_PERMISSION:
+ return parseDate(SECURITY_PATCH).before(MANAGE_COMPANION_DEVICES_PATCH_DATE);
+ default:
+ return false;
+ }
+ }
+
+ private class ExpectedPermissionInfo {
+ final @NonNull String name;
+ final @Nullable String group;
+ final @Nullable String backgroundPermission;
+ final int flags;
+ final int protectionLevel;
+
+ private ExpectedPermissionInfo(@NonNull String name, @Nullable String group,
+ @Nullable String backgroundPermission, int flags, int protectionLevel) {
+ this.name = name;
+ this.group = group;
+ this.backgroundPermission = backgroundPermission;
+ this.flags = flags;
+ this.protectionLevel = protectionLevel;
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PrivappPermissionsTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PrivappPermissionsTest.java
new file mode 100644
index 000000000..f33e8a6e6
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/PrivappPermissionsTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.permissionpolicy.cts;
+
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+
+import static com.google.common.collect.Maps.filterValues;
+import static com.google.common.collect.Sets.difference;
+import static com.google.common.collect.Sets.intersection;
+import static com.google.common.collect.Sets.newHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.PropertyUtil;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Tests enforcement of signature|privileged permission whitelist:
+ * <ul>
+ * <li>Report what is granted into the CTS log
+ * <li>Ensure all priv permissions are exclusively granted to applications declared in
+ * &lt;privapp-permissions&gt;
+ * </ul>
+ */
+@AppModeFull(reason = "This test test platform properties, not capabilities of an apps")
+@RunWith(AndroidJUnit4.class)
+public class PrivappPermissionsTest {
+
+ private static final boolean DEBUG = false;
+
+ private static final String TAG = "PrivappPermissionsTest";
+
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+
+ @Test
+ public void privappPermissionsMustBeEnforced() {
+ assertEquals("ro.control_privapp_permissions is not set to enforce",
+ "enforce", PropertyUtil.getProperty("ro.control_privapp_permissions"));
+ }
+
+ @Test
+ public void privappPermissionsNeedToBeWhitelisted() throws Exception {
+ Set<String> platformPrivPermissions = new HashSet<>();
+ PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ PackageInfo platformPackage = pm.getPackageInfo(PLATFORM_PACKAGE_NAME,
+ PackageManager.GET_PERMISSIONS);
+
+ for (PermissionInfo permission : platformPackage.permissions) {
+ int protectionLevel = permission.protectionLevel;
+ if ((protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
+ platformPrivPermissions.add(permission.name);
+ }
+ }
+
+ List<PackageInfo> installedPackages = pm
+ .getInstalledPackages(MATCH_UNINSTALLED_PACKAGES | GET_PERMISSIONS);
+ installedPackages.sort(Comparator.comparing(p -> p.packageName));
+
+ Map<String, Set<String>> packagesGrantedNotInWhitelist = new HashMap<>();
+ Map<String, Set<String>> packagesNotGrantedNotRemovedNotInDenylist = new HashMap<>();
+ for (PackageInfo pkg : installedPackages) {
+ String packageName = pkg.packageName;
+ if (!pkg.applicationInfo.isPrivilegedApp()
+ || PLATFORM_PACKAGE_NAME.equals(packageName)) {
+ continue;
+ }
+
+ PackageInfo factoryPkg = pm
+ .getPackageInfo(packageName, MATCH_FACTORY_ONLY | GET_PERMISSIONS
+ | MATCH_UNINSTALLED_PACKAGES);
+
+ assertNotNull("No system image version found for " + packageName, factoryPkg);
+
+ Set<String> factoryRequestedPrivPermissions;
+ if (factoryPkg.requestedPermissions == null) {
+ factoryRequestedPrivPermissions = Collections.emptySet();
+ } else {
+ factoryRequestedPrivPermissions = intersection(
+ newHashSet(factoryPkg.requestedPermissions), platformPrivPermissions);
+ }
+
+ Map<String, Boolean> requestedPrivPermissions = new ArrayMap<>();
+ if (pkg.requestedPermissions != null) {
+ for (int i = 0; i < pkg.requestedPermissions.length; i++) {
+ String permission = pkg.requestedPermissions[i];
+ if (platformPrivPermissions.contains(permission)) {
+ requestedPrivPermissions.put(permission,
+ (pkg.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+ != 0);
+ }
+ }
+ }
+
+ // If an app is requesting any privileged permissions, log the details and verify
+ // that granted permissions are whitelisted
+ if (!factoryRequestedPrivPermissions.isEmpty() && !requestedPrivPermissions.isEmpty()) {
+ Set<String> granted = filterValues(requestedPrivPermissions,
+ isGranted -> isGranted).keySet();
+
+ Set<String> factoryNotGranted = difference(factoryRequestedPrivPermissions,
+ granted);
+
+ // priv permissions that the system package requested, but the current package not
+ // anymore
+ Set<String> removed = difference(factoryRequestedPrivPermissions,
+ requestedPrivPermissions.keySet());
+
+ Set<String> whitelist = getPrivAppPermissions(packageName);
+ Set<String> denylist = getPrivAppDenyPermissions(packageName);
+
+ if (DEBUG) {
+ String msg = "Application " + packageName + "\n"
+ + " Factory requested permissions:\n"
+ + getPrintableSet(" ", factoryRequestedPrivPermissions)
+ + " Granted:\n"
+ + getPrintableSet(" ", granted)
+ + " Removed:\n"
+ + getPrintableSet(" ", removed)
+ + " Whitelisted:\n"
+ + getPrintableSet(" ", whitelist)
+ + " Denylisted:\n"
+ + getPrintableSet(" ", denylist)
+ + " Factory not granted:\n"
+ + getPrintableSet(" ", factoryNotGranted);
+
+ for (String line : msg.split("\n")) {
+ Log.i(TAG, line);
+
+ // Prevent log from truncating output
+ Thread.sleep(10);
+ }
+ }
+
+ Set<String> grantedNotInWhitelist = difference(granted, whitelist);
+ Set<String> factoryNotGrantedNotRemovedNotInDenylist = difference(difference(
+ factoryNotGranted, removed), denylist);
+
+ if (!grantedNotInWhitelist.isEmpty()) {
+ packagesGrantedNotInWhitelist.put(packageName, grantedNotInWhitelist);
+ }
+
+ if (!factoryNotGrantedNotRemovedNotInDenylist.isEmpty()) {
+ packagesNotGrantedNotRemovedNotInDenylist.put(packageName,
+ factoryNotGrantedNotRemovedNotInDenylist);
+ }
+ }
+ }
+ StringBuilder message = new StringBuilder();
+ if (!packagesGrantedNotInWhitelist.isEmpty()) {
+ message.append("Not whitelisted permissions are granted: "
+ + packagesGrantedNotInWhitelist.toString());
+ }
+ if (!packagesNotGrantedNotRemovedNotInDenylist.isEmpty()) {
+ if (message.length() != 0) {
+ message.append(", ");
+ }
+ message.append("Requested permissions not granted: "
+ + packagesNotGrantedNotRemovedNotInDenylist.toString());
+ }
+ if (!packagesGrantedNotInWhitelist.isEmpty()
+ || !packagesNotGrantedNotRemovedNotInDenylist.isEmpty()) {
+ fail(message.toString());
+ }
+ }
+
+ private <T> String getPrintableSet(String indendation, Set<T> set) {
+ if (set.isEmpty()) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ for (T e : new TreeSet<>(set)) {
+ if (!TextUtils.isEmpty(e.toString().trim())) {
+ sb.append(indendation);
+ sb.append(e);
+ sb.append("\n");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private Set<String> getPrivAppPermissions(String packageName) throws IOException {
+ String output = SystemUtil.runShellCommand(
+ InstrumentationRegistry.getInstrumentation(),
+ "cmd package get-privapp-permissions " + packageName).trim();
+ if (output.startsWith("{") && output.endsWith("}")) {
+ String[] split = output.substring(1, output.length() - 1).split("\\s*,\\s*");
+ return new LinkedHashSet<>(Arrays.asList(split));
+ }
+ return Collections.emptySet();
+ }
+
+ private Set<String> getPrivAppDenyPermissions(String packageName) throws IOException {
+ String output = SystemUtil.runShellCommand(
+ InstrumentationRegistry.getInstrumentation(),
+ "cmd package get-privapp-deny-permissions " + packageName).trim();
+ if (output.startsWith("{") && output.endsWith("}")) {
+ String[] split = output.substring(1, output.length() - 1).split("\\s*,\\s*");
+ return new LinkedHashSet<>(Arrays.asList(split));
+ }
+ return Collections.emptySet();
+ }
+
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ProtectedBroadcastsTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ProtectedBroadcastsTest.java
new file mode 100644
index 000000000..71c990441
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/ProtectedBroadcastsTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import android.content.Intent;
+import android.content.RestrictionsManager;
+import android.content.pm.PackageManager;
+import android.test.AndroidTestCase;
+
+/**
+ * Verify that applications can not send protected broadcasts.
+ */
+public class ProtectedBroadcastsTest extends AndroidTestCase {
+ private static final String BROADCASTS[] = new String[] {
+ Intent.ACTION_SCREEN_OFF,
+ Intent.ACTION_SCREEN_ON,
+ Intent.ACTION_USER_PRESENT,
+ Intent.ACTION_TIME_TICK,
+ Intent.ACTION_TIMEZONE_CHANGED,
+ Intent.ACTION_BOOT_COMPLETED,
+ Intent.ACTION_PACKAGE_INSTALL,
+ Intent.ACTION_PACKAGE_ADDED,
+ Intent.ACTION_PACKAGE_REPLACED,
+ Intent.ACTION_PACKAGE_REMOVED,
+ Intent.ACTION_PACKAGE_CHANGED,
+ Intent.ACTION_PACKAGE_RESTARTED,
+ Intent.ACTION_PACKAGE_DATA_CLEARED,
+ Intent.ACTION_UID_REMOVED,
+ Intent.ACTION_CONFIGURATION_CHANGED,
+ Intent.ACTION_BATTERY_CHANGED,
+ Intent.ACTION_BATTERY_LOW,
+ Intent.ACTION_BATTERY_OKAY,
+ Intent.ACTION_POWER_CONNECTED,
+ Intent.ACTION_POWER_DISCONNECTED,
+ Intent.ACTION_SHUTDOWN,
+ Intent.ACTION_DEVICE_STORAGE_LOW,
+ Intent.ACTION_DEVICE_STORAGE_OK,
+ Intent.ACTION_REBOOT,
+ "com.android.server.WifiManager.action.START_SCAN",
+ "com.android.server.WifiManager.action.DELAYED_DRIVER_STOP",
+ "android.net.wifi.WIFI_STATE_CHANGED",
+ "android.net.wifi.WIFI_AP_STATE_CHANGED",
+ "android.net.wifi.SCAN_RESULTS",
+ "android.net.wifi.RSSI_CHANGED",
+ "android.net.wifi.STATE_CHANGE",
+ "android.net.wifi.LINK_CONFIGURATION_CHANGED",
+ "android.net.wifi.CONFIGURED_NETWORKS_CHANGE",
+ "android.net.wifi.supplicant.CONNECTION_CHANGE",
+ "android.net.wifi.supplicant.STATE_CHANGE",
+ "android.net.wifi.p2p.STATE_CHANGED",
+ "android.net.wifi.p2p.DISCOVERY_STATE_CHANGE",
+ "android.net.wifi.p2p.THIS_DEVICE_CHANGED",
+ "android.net.wifi.p2p.PEERS_CHANGED",
+ "android.net.wifi.p2p.CONNECTION_STATE_CHANGE",
+ "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED",
+ "android.net.conn.TETHER_STATE_CHANGED",
+ "android.net.conn.INET_CONDITION_ACTION",
+ "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED",
+ RestrictionsManager.ACTION_PERMISSION_RESPONSE_RECEIVED,
+ RestrictionsManager.ACTION_REQUEST_PERMISSION
+ };
+
+ private static final String BROADCASTS_TELEPHONY[] = new String[] {
+ Intent.ACTION_NEW_OUTGOING_CALL,
+ "android.intent.action.SERVICE_STATE",
+ "android.intent.action.SIG_STR",
+ "android.intent.action.RADIO_TECHNOLOGY",
+ "android.intent.action.ANY_DATA_STATE",
+ "android.intent.action.ACTION_MDN_STATE_CHANGED",
+ "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED",
+ "android.intent.action.SIM_STATE_CHANGED",
+ "android.telephony.action.SERVICE_PROVIDERS_UPDATED",
+ "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED",
+ "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED",
+ "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS",
+ };
+
+ /**
+ * Verify that protected broadcast actions can't be sent.
+ */
+ public void testSendProtectedBroadcasts() {
+ for (String action : BROADCASTS) {
+ try {
+ Intent intent = new Intent(action);
+ getContext().sendBroadcast(intent);
+ fail("expected security exception broadcasting action: " + action);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+ }
+ }
+
+ public void testSendProtectedTelephonyBroadcasts() {
+ if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ for (String action : BROADCASTS_TELEPHONY) {
+ try {
+ Intent intent = new Intent(action);
+ getContext().sendBroadcast(intent);
+ fail("expected security exception broadcasting telephony action: " + action);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedPermissionsTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedPermissionsTest.java
new file mode 100644
index 000000000..5f396c49c
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedPermissionsTest.java
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2019 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 andf
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.READ_SMS;
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.isPermissionGranted;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.Manifest.permission;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.Session;
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SystemUserOnly;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+
+/**
+ * Tests for restricted permission behaviors.
+ */
+public class RestrictedPermissionsTest {
+ private static final String APK_USES_LOCATION_22 =
+ "/data/local/tmp/cts-permissionpolicy/CtsLocationPermissionsUserSdk22.apk";
+
+ private static final String APK_USES_LOCATION_29 =
+ "/data/local/tmp/cts-permissionpolicy/CtsLocationPermissionsUserSdk29.apk";
+
+ private static final String APK_USES_SMS_CALL_LOG_22 =
+ "/data/local/tmp/cts-permissionpolicy/CtsSMSCallLogPermissionsUserSdk22.apk";
+
+ private static final String APK_NAME_USES_SMS_CALL_LOG_29 =
+ "CtsSMSCallLogPermissionsUserSdk29.apk";
+
+ private static final String APK_USES_SMS_CALL_LOG_29 =
+ "/data/local/tmp/cts-permissionpolicy/CtsSMSCallLogPermissionsUserSdk29.apk";
+
+ private static final String APK_USES_STORAGE_DEFAULT_29 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk29.apk";
+
+ private static final String PKG = "android.permissionpolicy.cts.restrictedpermissionuser";
+
+ private static final String APK_USES_SMS_RESTRICTED_SHARED_UID =
+ "/data/local/tmp/cts-permissionpolicy/CtsSMSRestrictedWithSharedUid.apk";
+
+ private static final String PKG_USES_SMS_RESTRICTED_SHARED_UID =
+ "android.permissionpolicy.cts.smswithshareduid.restricted";
+
+ private static final String APK_USES_SMS_NOT_RESTRICTED_SHARED_UID =
+ "/data/local/tmp/cts-permissionpolicy/CtsSMSNotRestrictedWithSharedUid.apk";
+
+ private static final String PKG_USES_SMS_NOT_RESTRICTED_SHARED_UID =
+ "android.permissionpolicy.cts.smswithshareduid.notrestricted";
+
+ private static final long UI_TIMEOUT = 5000L;
+
+ private static @NonNull BroadcastReceiver sCommandReceiver;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ sCommandReceiver = new CommandBroadcastReceiver();
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction("installRestrictedPermissionUserApp");
+ intentFilter.addAction("uninstallApp");
+ getContext().registerReceiver(sCommandReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ getContext().unregisterReceiver(sCommandReceiver);
+ }
+
+ @Test
+ @AppModeFull
+ public void testDefaultAllRestrictedPermissionsWhitelistedAtInstall29() throws Exception {
+ // Install with no changes to whitelisted permissions, not attempting to grant.
+ installRestrictedPermissionUserApp(null /*whitelistedPermissions*/,
+ Collections.EMPTY_SET /*grantedPermissions*/);
+
+ // All restricted permission should be whitelisted.
+ assertAllRestrictedPermissionWhitelisted();
+
+ // No restricted permission should be granted.
+ assertNoRestrictedPermissionGranted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testSomeRestrictedPermissionsWhitelistedAtInstall29() throws Exception {
+ // Whitelist only these permissions.
+ final Set<String> whitelistedPermissions = new ArraySet<>(2);
+ whitelistedPermissions.add(Manifest.permission.SEND_SMS);
+ whitelistedPermissions.add(Manifest.permission.READ_CALL_LOG);
+
+ // Install with some whitelisted permissions, not attempting to grant.
+ installRestrictedPermissionUserApp(whitelistedPermissions,
+ Collections.EMPTY_SET /*grantedPermissions*/);
+
+ // Some restricted permission should be whitelisted.
+ assertRestrictedPermissionWhitelisted(whitelistedPermissions);
+
+ // No restricted permission should be granted.
+ assertNoRestrictedPermissionGranted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testNoneRestrictedPermissionWhitelistedAtInstall29() throws Exception {
+ // Install with all whitelisted permissions, not attempting to grant.
+ installRestrictedPermissionUserApp(Collections.emptySet(),
+ Collections.EMPTY_SET /*grantedPermissions*/);
+
+ // No restricted permission should be whitelisted.
+ assertNoRestrictedPermissionWhitelisted();
+
+ // No restricted permission should be granted.
+ assertNoRestrictedPermissionGranted();
+ }
+
+ @Test
+ @AppModeFull
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_SMS user restriction")
+ public void testDefaultAllRestrictedPermissionsWhitelistedAtInstall22() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_SMS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ String bypassLowTargetSdkFlag = "";
+ if (SdkLevel.isAtLeastU()) {
+ bypassLowTargetSdkFlag = " --bypass-low-target-sdk-block";
+ }
+
+ // Install with no changes to whitelisted permissions
+ runShellCommandOrThrow("pm install" + bypassLowTargetSdkFlag
+ + " -g --force-queryable " + APK_USES_SMS_CALL_LOG_22);
+
+ // All restricted permission should be whitelisted.
+ assertAllRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_OUTGOING_CALLS user restriction")
+ public void testSomeRestrictedPermissionsWhitelistedAtInstall22() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_OUTGOING_CALLS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ // Whitelist only these permissions.
+ final Set<String> whitelistedPermissions = new ArraySet<>(2);
+ whitelistedPermissions.add(Manifest.permission.SEND_SMS);
+ whitelistedPermissions.add(Manifest.permission.READ_CALL_LOG);
+
+ // Install with some whitelisted permissions
+ installApp(APK_USES_SMS_CALL_LOG_22, whitelistedPermissions, null /*grantedPermissions*/);
+
+ // Some restricted permission should be whitelisted.
+ assertRestrictedPermissionWhitelisted(whitelistedPermissions);
+ }
+
+ @Test
+ @AppModeFull
+ public void testNoneRestrictedPermissionWhitelistedAtInstall22() throws Exception {
+ // Install with all whitelisted permissions
+ installApp(APK_USES_SMS_CALL_LOG_22, Collections.emptySet(),
+ null /*grantedPermissions*/);
+
+ // No restricted permission should be whitelisted.
+ assertNoRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testLocationBackgroundPermissionWhitelistedAtInstall29() throws Exception {
+ installApp(APK_USES_LOCATION_29, null, new ArraySet<>(Arrays.asList(ACCESS_FINE_LOCATION,
+ ACCESS_BACKGROUND_LOCATION)));
+ assertAllRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testLocationBackgroundPermissionNotWhitelistedAtInstall29() throws Exception {
+ installApp(APK_USES_LOCATION_29, Collections.emptySet(),
+ Collections.singleton(ACCESS_FINE_LOCATION));
+ assertNoRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testLocationBackgroundPermissionWhitelistedAtInstall22() throws Exception {
+ installApp(APK_USES_LOCATION_22, null, new ArraySet<>(Arrays.asList(ACCESS_FINE_LOCATION,
+ ACCESS_BACKGROUND_LOCATION)));
+ assertAllRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testLocationBackgroundPermissionNotWhitelistedAtInstall22() throws Exception {
+ installApp(APK_USES_LOCATION_22, Collections.emptySet(),
+ Collections.singleton(ACCESS_FINE_LOCATION));
+ assertNoRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_OUTGOING_CALLS user restriction")
+ public void testSomeRestrictedPermissionsGrantedAtInstall() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_OUTGOING_CALLS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ // Grant only these permissions.
+ final Set<String> grantedPermissions = new ArraySet<>(1);
+ grantedPermissions.add(Manifest.permission.SEND_SMS);
+ grantedPermissions.add(Manifest.permission.READ_CALL_LOG);
+
+ // Install with no whitelisted permissions attempting to grant.
+ installRestrictedPermissionUserApp(null /*whitelistedPermissions*/, grantedPermissions);
+
+ // All restricted permission should be whitelisted.
+ assertAllRestrictedPermissionWhitelisted();
+
+ // Some restricted permission should be granted.
+ assertRestrictedPermissionGranted(grantedPermissions);
+ }
+
+ @Test
+ @AppModeFull
+ public void testCanGrantSoftRestrictedNotWhitelistedPermissions() throws Exception {
+ try {
+ final Set<String> grantedPermissions = new ArraySet<>();
+ grantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ grantedPermissions.add(permission.WRITE_EXTERNAL_STORAGE);
+
+ installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet(), grantedPermissions);
+
+ assertRestrictedPermissionGranted(grantedPermissions);
+ } finally {
+ uninstallApp();
+ }
+ }
+
+ @Test
+ @AppModeFull
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_SMS user restriction")
+ public void testAllRestrictedPermissionsGrantedAtInstall() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_SMS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ // Install with whitelisted permissions attempting to grant.
+ installRestrictedPermissionUserApp(null /*whitelistedPermissions*/,
+ null);
+
+ // All restricted permission should be whitelisted.
+ assertAllRestrictedPermissionWhitelisted();
+
+ // Some restricted permission should be granted.
+ assertAllRestrictedPermissionGranted();
+ }
+
+ @Test
+ @AppModeFull
+ public void testWhitelistAccessControl() throws Exception {
+ // Install with no whitelisted permissions not attempting to grant.
+ installRestrictedPermissionUserApp(Collections.emptySet(), null);
+
+ assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM);
+
+ assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
+
+ assertWeCannotReadOrWriteWhileShellCanReadAndWrite(
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ }
+
+ @Test
+ @AppModeFull
+ public void onSideLoadRestrictedPermissionsWhitelistingDefault() throws Exception {
+ installRestrictedPermissionUserApp(new SessionParams(SessionParams.MODE_FULL_INSTALL));
+
+ // All restricted permissions whitelisted on side-load by default
+ assertAllRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ public void onSideLoadAllRestrictedPermissionsWhitelisted() throws Exception {
+ SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ params.setWhitelistedRestrictedPermissions(SessionParams.RESTRICTED_PERMISSIONS_ALL);
+
+ installRestrictedPermissionUserApp(params);
+
+ assertAllRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ @FlakyTest
+ public void onSideLoadWhitelistSomePermissions() throws Exception {
+ Set<String> whitelistedPermissions = new ArraySet<>();
+ whitelistedPermissions.add(Manifest.permission.SEND_SMS);
+ whitelistedPermissions.add(Manifest.permission.READ_CALL_LOG);
+
+ SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ params.setWhitelistedRestrictedPermissions(whitelistedPermissions);
+
+ installRestrictedPermissionUserApp(params);
+
+ assertRestrictedPermissionWhitelisted(whitelistedPermissions);
+ }
+
+ @Test
+ @AppModeFull
+ @FlakyTest
+ public void onSideLoadWhitelistNoPermissions() throws Exception {
+ SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+ params.setWhitelistedRestrictedPermissions(Collections.emptySet());
+
+ installRestrictedPermissionUserApp(params);
+
+ assertNoRestrictedPermissionWhitelisted();
+ }
+
+ @Test
+ @AppModeFull
+ @SystemUserOnly(reason = "Secondary users have the DISALLOW_SMS user restriction")
+ public void shareUidBetweenRestrictedAndNotRestrictedApp() throws Exception {
+ Assume.assumeTrue("Secondary users have the DISALLOW_SMS user restriction",
+ UserHandle.SYSTEM.equals(Process.myUserHandle()));
+
+ runShellCommandOrThrow(
+ "pm install -g --force-queryable --restrict-permissions "
+ + APK_USES_SMS_RESTRICTED_SHARED_UID);
+ runShellCommandOrThrow("pm install -g --force-queryable "
+ + APK_USES_SMS_NOT_RESTRICTED_SHARED_UID);
+
+ eventually(
+ () -> assertThat(isGranted(PKG_USES_SMS_RESTRICTED_SHARED_UID, READ_SMS)).isTrue());
+ // The apps share a UID, hence the whitelisting is shared too
+ assertThat(isGranted(PKG_USES_SMS_NOT_RESTRICTED_SHARED_UID, READ_SMS)).isTrue();
+ }
+
+ private static void installRestrictedPermissionUserApp(@NonNull SessionParams params)
+ throws Exception {
+ final CountDownLatch installLatch = new CountDownLatch(1);
+
+ // Create an install result receiver.
+ final BroadcastReceiver installReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE_INVALID)
+ == PackageInstaller.STATUS_SUCCESS) {
+ installLatch.countDown();
+ }
+ }
+ };
+
+ // Register the result receiver.
+ final String action = "android.permissionpolicy.cts.ACTION_INSTALL_COMMIT";
+ final IntentFilter intentFilter = new IntentFilter(action);
+ getContext().registerReceiver(installReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
+
+ try {
+ // Create a session.
+ final PackageInstaller packageInstaller = getContext()
+ .getPackageManager().getPackageInstaller();
+ final int sessionId = packageInstaller.createSession(params);
+ final Session session = packageInstaller.openSession(sessionId);
+
+ // Write the apk.
+ try (
+ InputStream in = new BufferedInputStream(new FileInputStream(
+ new File(APK_USES_SMS_CALL_LOG_29)));
+ OutputStream out = session.openWrite(
+ APK_NAME_USES_SMS_CALL_LOG_29, 0, -1);
+ ) {
+ final byte[] buf = new byte[8192];
+ int size;
+ while ((size = in.read(buf)) != -1) {
+ out.write(buf, 0, size);
+ }
+ }
+
+ final Intent intent = new Intent(action);
+ intent.setPackage("android.permissionpolicy.cts");
+ final IntentSender intentSender = PendingIntent.getBroadcast(getContext(),
+ 1, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE)
+ .getIntentSender();
+
+ // Commit as shell to avoid confirm UI
+ runWithShellPermissionIdentity(() -> {
+ session.commit(intentSender);
+ installLatch.await(UI_TIMEOUT, TimeUnit.MILLISECONDS);
+ });
+ } finally {
+ getContext().unregisterReceiver(installReceiver);
+ }
+ }
+
+ private void assertWeCannotReadOrWriteWhileShellCanReadAndWrite(int whitelist)
+ throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ try {
+ packageManager.getWhitelistedRestrictedPermissions(PKG, whitelist);
+ fail();
+ } catch (SecurityException expected) {
+ /*ignore*/
+ }
+ try {
+ packageManager.addWhitelistedRestrictedPermission(PKG,
+ permission.SEND_SMS, whitelist);
+ fail();
+ } catch (SecurityException expected) {
+ /*ignore*/
+ }
+ runWithShellPermissionIdentity(() -> {
+ packageManager.addWhitelistedRestrictedPermission(PKG,
+ permission.SEND_SMS, whitelist);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ whitelist)).contains(permission.SEND_SMS);
+ packageManager.removeWhitelistedRestrictedPermission(PKG,
+ permission.SEND_SMS, whitelist);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ whitelist)).doesNotContain(permission.SEND_SMS);
+ });
+ }
+
+ private @NonNull Set<String> getPermissionsOfAppWithAnyOfFlags(int flags) throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final Set<String> restrictedPermissions = new ArraySet<>();
+ for (String permission : getRequestedPermissionsOfApp()) {
+ PermissionInfo permInfo = packageManager.getPermissionInfo(permission, 0);
+
+ if ((permInfo.flags & flags) != 0) {
+ restrictedPermissions.add(permission);
+ }
+ }
+ return restrictedPermissions;
+ }
+
+ private @NonNull Set<String> getRestrictedPermissionsOfApp() throws Exception {
+ return getPermissionsOfAppWithAnyOfFlags(
+ PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED);
+ }
+
+ private @NonNull String[] getRequestedPermissionsOfApp() throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final PackageInfo packageInfo = packageManager.getPackageInfo(PKG,
+ PackageManager.GET_PERMISSIONS);
+ return packageInfo.requestedPermissions;
+ }
+
+ private void assertAllRestrictedPermissionWhitelisted() throws Exception {
+ assertRestrictedPermissionWhitelisted(getRestrictedPermissionsOfApp());
+ }
+
+ private void assertNoRestrictedPermissionWhitelisted() throws Exception {
+ assertRestrictedPermissionWhitelisted(
+ Collections.EMPTY_SET /*expectedWhitelistedPermissions*/);
+ }
+
+ /**
+ * Assert that the passed in restrictions are whitelisted and that their app-op is set
+ * correctly.
+ *
+ * @param expectedWhitelistedPermissions The expected white listed permissions
+ */
+ private void assertRestrictedPermissionWhitelisted(
+ @NonNull Set<String> expectedWhitelistedPermissions) throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ eventually(() -> runWithShellPermissionIdentity(() -> {
+ final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ final PackageInfo packageInfo = packageManager.getPackageInfo(PKG,
+ PackageManager.GET_PERMISSIONS);
+
+ final Set<String> whitelistedPermissions = packageManager
+ .getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
+ | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
+ | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
+
+ assertThat(whitelistedPermissions).isNotNull();
+ assertWithMessage("Whitelisted permissions").that(whitelistedPermissions)
+ .containsExactlyElementsIn(expectedWhitelistedPermissions);
+
+ // Also assert that apps ops are properly set
+ for (String permission : getRestrictedPermissionsOfApp()) {
+ String op = AppOpsManager.permissionToOp(permission);
+ ArraySet<Integer> possibleModes = new ArraySet<>();
+
+ if (permission.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
+ op = AppOpsManager.OPSTR_FINE_LOCATION;
+
+ // If permission is denied app-op might be allowed/fg or ignored. It does
+ // not matter. If permission is granted, it has to be allowed/fg.
+ if (isPermissionGranted(PKG, Manifest.permission.ACCESS_FINE_LOCATION)) {
+ if (expectedWhitelistedPermissions.contains(permission)
+ && isPermissionGranted(PKG, permission)) {
+ possibleModes.add(AppOpsManager.MODE_ALLOWED);
+ } else {
+ possibleModes.add(AppOpsManager.MODE_FOREGROUND);
+ }
+ } else {
+ possibleModes.add(AppOpsManager.MODE_IGNORED);
+ possibleModes.add(AppOpsManager.MODE_ALLOWED);
+ possibleModes.add(AppOpsManager.MODE_FOREGROUND);
+ }
+ } else {
+ if (expectedWhitelistedPermissions.contains(permission)) {
+ // If permission is denied app-op might be allowed or ignored. It does not
+ // matter. If permission is granted, it has to be allowed.
+ possibleModes.add(AppOpsManager.MODE_ALLOWED);
+ if (!isPermissionGranted(PKG, permission)) {
+ possibleModes.add(AppOpsManager.MODE_IGNORED);
+ }
+ } else {
+ possibleModes.add(AppOpsManager.MODE_IGNORED);
+ }
+ }
+
+ assertWithMessage(op).that(appOpsManager.unsafeCheckOpRawNoThrow(op,
+ packageInfo.applicationInfo.uid, PKG)).isIn(possibleModes);
+ }
+ }));
+ }
+
+ private void assertAllRestrictedPermissionGranted() throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final PackageInfo packageInfo = packageManager.getPackageInfo(
+ PKG, PackageManager.GET_PERMISSIONS);
+ if (packageInfo.requestedPermissions != null) {
+ final int permissionCount = packageInfo.requestedPermissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ final String permission = packageInfo.requestedPermissions[i];
+ final PermissionInfo permissionInfo = packageManager.getPermissionInfo(
+ permission, 0);
+ if ((permissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0) {
+ assertThat((packageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED)).isNotEqualTo(0);
+ }
+ }
+ }
+ }
+
+ private void assertNoRestrictedPermissionGranted() throws Exception {
+ assertRestrictedPermissionGranted(Collections.EMPTY_SET);
+ }
+
+ private void assertRestrictedPermissionGranted(@NonNull Set<String> expectedGrantedPermissions)
+ throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final PackageInfo packageInfo = packageManager.getPackageInfo(
+ PKG, PackageManager.GET_PERMISSIONS);
+ if (packageInfo.requestedPermissions != null) {
+ final int permissionCount = packageInfo.requestedPermissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ final String permission = packageInfo.requestedPermissions[i];
+ final PermissionInfo permissionInfo = packageManager.getPermissionInfo(
+ permission, 0);
+ if ((permissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+ || (permissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+ if (expectedGrantedPermissions.contains(permission)) {
+ assertThat((packageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED)).isNotEqualTo(0);
+ } else {
+ assertThat((packageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED)).isEqualTo(0);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Install {@link #APK_USES_SMS_CALL_LOG_29}.
+ *
+ * @param whitelistedPermissions The permission to be whitelisted. {@code null} == all
+ * @param grantedPermissions The permission to be granted. {@code null} == all
+ */
+ private void installRestrictedPermissionUserApp(@Nullable Set<String> whitelistedPermissions,
+ @Nullable Set<String> grantedPermissions) throws Exception {
+ installApp(APK_USES_SMS_CALL_LOG_29, whitelistedPermissions, grantedPermissions);
+ }
+
+ /**
+ * Install app and grant all permission.
+ *
+ * @param app The app to be installed
+ * @param whitelistedPermissions The permission to be whitelisted. {@code null} == all
+ */
+ private void installApp(@NonNull String app, @Nullable Set<String> whitelistedPermissions)
+ throws Exception {
+ installApp(app, whitelistedPermissions, null /*grantedPermissions*/);
+ }
+
+ /**
+ * Install an app.
+ *
+ * @param app The app to be installed
+ * @param whitelistedPermissions The permission to be whitelisted. {@code null} == all
+ * @param grantedPermissions The permission to be granted. {@code null} == all
+ */
+ private void installApp(@NonNull String app, @Nullable Set<String> whitelistedPermissions,
+ @Nullable Set<String> grantedPermissions) throws Exception {
+ String bypassLowTargetSdkFlag = "";
+ if (SdkLevel.isAtLeastU()) {
+ bypassLowTargetSdkFlag = " --bypass-low-target-sdk-block";
+ }
+
+ // Install the app and whitelist/grant all permission if requested.
+ String installResult = runShellCommandOrThrow("pm install -r --force-queryable"
+ + bypassLowTargetSdkFlag + " --restrict-permissions " + app);
+ assertThat(installResult.trim()).isEqualTo("Success");
+
+ final Set<String> adjustedWhitelistedPermissions;
+ if (whitelistedPermissions == null) {
+ adjustedWhitelistedPermissions = getRestrictedPermissionsOfApp();
+ } else {
+ adjustedWhitelistedPermissions = whitelistedPermissions;
+ }
+
+ final Set<String> adjustedGrantedPermissions;
+ if (grantedPermissions == null) {
+ adjustedGrantedPermissions = getRestrictedPermissionsOfApp();
+ } else {
+ adjustedGrantedPermissions = grantedPermissions;
+ }
+
+ // Whitelist subset of permissions if requested
+ runWithShellPermissionIdentity(() -> {
+ final PackageManager packageManager = getContext().getPackageManager();
+ for (String permission : adjustedWhitelistedPermissions) {
+ packageManager.addWhitelistedRestrictedPermission(PKG, permission,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ }
+ });
+
+ // Grant subset of permissions if requested
+ runWithShellPermissionIdentity(() -> {
+ final PackageManager packageManager = getContext().getPackageManager();
+ for (String permission : adjustedGrantedPermissions) {
+ packageManager.grantRuntimePermission(PKG, permission,
+ getContext().getUser());
+ packageManager.updatePermissionFlags(permission, PKG,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0, getContext().getUser());
+ }
+ });
+
+ // Mark all permissions as reviewed as for pre-22 apps the restriction state might not be
+ // applied until reviewed
+ runWithShellPermissionIdentity(() -> {
+ final PackageManager packageManager = getContext().getPackageManager();
+ for (String permission : getRequestedPermissionsOfApp()) {
+ packageManager.updatePermissionFlags(permission, PKG,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0,
+ getContext().getUser());
+ }
+ });
+ }
+
+ @After
+ public void uninstallApp() {
+ runShellCommand("pm uninstall " + PKG);
+ runShellCommand("pm uninstall " + PKG_USES_SMS_NOT_RESTRICTED_SHARED_UID);
+ runShellCommand("pm uninstall " + PKG_USES_SMS_RESTRICTED_SHARED_UID);
+ }
+
+ private static @NonNull Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
+ private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity();
+ try {
+ command.run();
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionSharedUidTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionSharedUidTest.java
new file mode 100644
index 000000000..d6ee7a66b
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionSharedUidTest.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OPSTR_LEGACY_STORAGE;
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permissionpolicy.cts.RestrictedStoragePermissionSharedUidTest.StorageState.DENIED;
+import static android.permissionpolicy.cts.RestrictedStoragePermissionSharedUidTest.StorageState.ISOLATED;
+import static android.permissionpolicy.cts.RestrictedStoragePermissionSharedUidTest.StorageState.NON_ISOLATED;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static java.lang.Integer.min;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+
+@AppModeFull(reason = "Instant apps cannot access other app's properties")
+@RunWith(Parameterized.class)
+public class RestrictedStoragePermissionSharedUidTest {
+ private static final String LOG_TAG =
+ RestrictedStoragePermissionSharedUidTest.class.getSimpleName();
+
+ public enum StorageState {
+ /** The app has non-isolated storage */
+ NON_ISOLATED,
+
+ /** The app has isolated storage */
+ ISOLATED,
+
+ /** The read-external-storage permission cannot be granted */
+ DENIED
+ }
+
+ /**
+ * An app that is tested
+ */
+ private static class TestApp {
+ private static @NonNull Context sContext =
+ InstrumentationRegistry.getInstrumentation().getContext();
+ private static @NonNull AppOpsManager sAppOpsManager =
+ sContext.getSystemService(AppOpsManager.class);
+ private static @NonNull PackageManager sPackageManager = sContext.getPackageManager();
+
+ private final String mApk;
+ private final String mPkg;
+
+ public final boolean isRestricted;
+ public final boolean hasRequestedLegacyExternalStorage;
+
+ TestApp(@NonNull String apk, @NonNull String pkg, boolean isRestricted,
+ @NonNull boolean hasRequestedLegacyExternalStorage) {
+ mApk = apk;
+ mPkg = pkg;
+
+ this.isRestricted = isRestricted;
+ this.hasRequestedLegacyExternalStorage = hasRequestedLegacyExternalStorage;
+ }
+
+ /**
+ * Assert that the read-external-storage permission was granted or not granted.
+ *
+ * @param expectGranted {@code true} if the permission is expected to be granted
+ */
+ void assertStoragePermGranted(boolean expectGranted) {
+ eventually(() -> assertWithMessage(this + " read storage granted").that(
+ isGranted(mPkg, READ_EXTERNAL_STORAGE)).isEqualTo(expectGranted));
+ }
+
+ /**
+ * Assert that the app has non-isolated storage
+ *
+ * @param expectGranted {@code true} if the app is expected to have non-isolated storage
+ */
+ void assertHasNotIsolatedStorage(boolean expectHasNotIsolatedStorage) {
+ eventually(() -> runWithShellPermissionIdentity(() -> {
+ int uid = sContext.getPackageManager().getPackageUid(mPkg, 0);
+ if (expectHasNotIsolatedStorage) {
+ assertWithMessage(this + " legacy storage mode").that(
+ sAppOpsManager.unsafeCheckOpRawNoThrow(OPSTR_LEGACY_STORAGE, uid,
+ mPkg)).isEqualTo(MODE_ALLOWED);
+ } else {
+ assertWithMessage(this + " legacy storage mode").that(
+ sAppOpsManager.unsafeCheckOpRawNoThrow(OPSTR_LEGACY_STORAGE, uid,
+ mPkg)).isNotEqualTo(MODE_ALLOWED);
+ }
+ }));
+ }
+
+ int getTargetSDK() throws Exception {
+ return sPackageManager.getApplicationInfo(mPkg, 0).targetSdkVersion;
+ }
+
+ void install() {
+ if (isRestricted) {
+ runShellCommandOrThrow(
+ "pm install -g --force-queryable --restrict-permissions " + mApk);
+ } else {
+ runShellCommandOrThrow("pm install -g --force-queryable " + mApk);
+ }
+ }
+
+ void uninstall() {
+ runShellCommand("pm uninstall " + mPkg);
+ }
+
+ @Override
+ public String toString() {
+ return mPkg.substring(PKG_PREFIX.length());
+ }
+ }
+
+ /**
+ * Placeholder for "no app". The properties are chosen that when combined with another app, the
+ * other app always decides the resulting property,
+ */
+ private static class NoApp extends TestApp {
+ NoApp() {
+ super("", PKG_PREFIX + "(none)", true, false);
+ }
+
+ void assertStoragePermGranted(boolean ignored) {
+ // empty
+ }
+
+ void assertHasNotIsolatedStorage(boolean ignored) {
+ // empty
+ }
+
+ @Override
+ int getTargetSDK() {
+ return 10000;
+ }
+
+ @Override
+ public void install() {
+ // empty
+ }
+
+ @Override
+ public void uninstall() {
+ // empty
+ }
+ }
+
+ private static final String APK_PATH = "/data/local/tmp/cts-permissionpolicy/";
+ private static final String PKG_PREFIX = "android.permissionpolicy.cts.legacystoragewithshareduid.";
+
+ private static final TestApp[] TEST_APPS = new TestApp[]{
+ new TestApp(APK_PATH + "CtsLegacyStorageNotIsolatedWithSharedUid.apk",
+ PKG_PREFIX + "notisolated", false, true),
+ new TestApp(APK_PATH + "CtsLegacyStorageIsolatedWithSharedUid.apk",
+ PKG_PREFIX + "isolated", false, false),
+ new TestApp(APK_PATH + "CtsLegacyStorageRestrictedWithSharedUid.apk",
+ PKG_PREFIX + "restricted", true, false),
+ new TestApp(APK_PATH + "CtsLegacyStorageRestrictedSdk28WithSharedUid.apk",
+ PKG_PREFIX + "restrictedsdk28", true, true),
+ new NoApp()};
+
+ /**
+ * First app to be tested. This is the first in an entry created by {@link
+ * #getTestAppCombinations}
+ */
+ @Parameter(0)
+ public @NonNull TestApp app1;
+
+ /**
+ * Second app to be tested. This is the second in an entry created by {@link
+ * #getTestAppCombinations}
+ */
+ @Parameter(1)
+ public @NonNull TestApp app2;
+
+ /**
+ * Run this test for all combination of two tests-apps out of {@link #TEST_APPS}. This includes
+ * the {@link NoApp}, i.e. we also test a single test-app by itself.
+ *
+ * @return All combinations of two test-apps
+ */
+ @Parameters(name = "{0} and {1}")
+ public static Iterable<Object[]> getTestAppCombinations() {
+ ArrayList<Object[]> parameters = new ArrayList<>();
+
+ for (int firstApp = 0; firstApp < TEST_APPS.length; firstApp++) {
+ for (int secondApp = firstApp + 1; secondApp < TEST_APPS.length; secondApp++) {
+ parameters.add(new Object[]{TEST_APPS[firstApp], TEST_APPS[secondApp]});
+ }
+ }
+
+ return parameters;
+ }
+
+ @Test
+ public void checkExceptedStorageStateForAppsSharingUid() throws Exception {
+ app1.install();
+ app2.install();
+
+ int targetSDK = min(app1.getTargetSDK(), app2.getTargetSDK());
+ boolean isRestricted = app1.isRestricted && app2.isRestricted;
+ boolean hasRequestedLegacyExternalStorage =
+ app1.hasRequestedLegacyExternalStorage || app2.hasRequestedLegacyExternalStorage;
+
+ StorageState expectedState;
+ if (isRestricted) {
+ if (targetSDK < Build.VERSION_CODES.Q) {
+ expectedState = DENIED;
+ } else {
+ expectedState = ISOLATED;
+ }
+ } else if (hasRequestedLegacyExternalStorage && targetSDK <= Build.VERSION_CODES.Q) {
+ expectedState = NON_ISOLATED;
+ } else {
+ expectedState = ISOLATED;
+ }
+
+ Log.i(LOG_TAG, "Expected state=" + expectedState);
+
+ app1.assertStoragePermGranted(expectedState != DENIED);
+ app2.assertStoragePermGranted(expectedState != DENIED);
+
+ if (expectedState != DENIED) {
+ app1.assertHasNotIsolatedStorage(expectedState == NON_ISOLATED);
+ app2.assertHasNotIsolatedStorage(expectedState == NON_ISOLATED);
+ }
+ }
+
+ @After
+ public void uninstallAllTestPackages() {
+ app1.uninstall();
+ app2.uninstall();
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionTest.java b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionTest.java
new file mode 100644
index 000000000..6a3b0711d
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RestrictedStoragePermissionTest.java
@@ -0,0 +1,755 @@
+/*
+ * 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.
+ */
+
+package android.permissionpolicy.cts;
+
+import static android.permission.cts.PermissionUtils.isGranted;
+import static android.permission.cts.PermissionUtils.isPermissionGranted;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.Manifest.permission;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.platform.test.annotations.AppModeFull;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.modules.utils.build.SdkLevel;
+
+import org.junit.After;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/** Tests for restricted storage-related permissions. */
+public class RestrictedStoragePermissionTest {
+ private static final String APK_USES_STORAGE_DEFAULT_22 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk22.apk";
+
+ private static final String APK_USES_STORAGE_DEFAULT_28 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk28.apk";
+
+ private static final String APK_USES_STORAGE_DEFAULT_29 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserDefaultSdk29.apk";
+
+ private static final String APK_USES_STORAGE_OPT_IN_22 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptInSdk22.apk";
+
+ private static final String APK_USES_STORAGE_OPT_IN_28 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptInSdk28.apk";
+
+ private static final String APK_USES_STORAGE_OPT_OUT_29 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptOutSdk29.apk";
+
+ private static final String APK_USES_STORAGE_OPT_OUT_30 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsUserOptOutSdk30.apk";
+
+ private static final String APK_USES_STORAGE_PRESERVED_OPT_OUT_30 =
+ "/data/local/tmp/cts-permissionpolicy/CtsStoragePermissionsPreservedUserOptOutSdk30.apk";
+
+ private static final String PKG = "android.permissionpolicy.cts.restrictedpermissionuser";
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk22DefaultWhitelistedHasFullAccess() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_22, null /*whitelistedPermissions*/);
+
+ // Check expected storage mode
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk22OptInWhitelistedHasIsolatedAccess() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_IN_22, null /*whitelistedPermissions*/);
+
+ // Check expected storage mode
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28DefaultWhitelistedHasFullAccess() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
+
+ // Check expected storage mode
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28OptInWhitelistedHasIsolatedAccess() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_IN_28, null /*whitelistedPermissions*/);
+
+ // Check expected storage mode
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29DefaultWhitelistedHasIsolatedAccess() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
+
+ // Check expected storage mode
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29DefaultNotWhitelistedHasIsolatedAccess() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_29, null /*whitelistedPermissions*/);
+
+ // Check expected storage mode
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29OptOutWhitelistedHasFullAccess() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null /*whitelistedPermissions*/);
+
+ // Check expected storage mode
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29OptOutNotWhitelistedHasIsolatedAccess() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
+
+ // Check expected storage mode
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29CanOptOutViaUpdate() throws Exception {
+ installApp(APK_USES_STORAGE_DEFAULT_29, null);
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29CanOptOutViaDowngradeTo28() throws Exception {
+ installApp(APK_USES_STORAGE_DEFAULT_29, null);
+ installApp(APK_USES_STORAGE_DEFAULT_28, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk30_cannotOptOut() throws Exception {
+ // Apps that target R and above cannot opt out of isolated storage.
+ installApp(APK_USES_STORAGE_OPT_OUT_30, null);
+
+ // Check expected storage mode
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28CanRemoveOptInViaUpdate() throws Exception {
+ installApp(APK_USES_STORAGE_OPT_IN_28, null);
+ installApp(APK_USES_STORAGE_DEFAULT_28, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28CanRemoveOptInByOptingOut() throws Exception {
+ installApp(APK_USES_STORAGE_OPT_IN_28, null);
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28DoesNotLoseAccessWhenOptingIn() throws Exception {
+ installApp(APK_USES_STORAGE_DEFAULT_28, null);
+ assertHasFullStorageAccess();
+ installApp(APK_USES_STORAGE_OPT_IN_28, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28DoesNotLoseAccessViaUpdate() throws Exception {
+ installApp(APK_USES_STORAGE_DEFAULT_28, null);
+ assertHasFullStorageAccess();
+ installApp(APK_USES_STORAGE_DEFAULT_29, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29DoesNotLoseAccessViaUpdate() throws Exception {
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+ assertHasFullStorageAccess();
+ installApp(APK_USES_STORAGE_DEFAULT_29, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29DoesNotLoseAccessWhenOptingIn() throws Exception {
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+ assertHasFullStorageAccess();
+ installApp(APK_USES_STORAGE_OPT_IN_28, null);
+
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk29LosesAccessViaUpdateToTargetSdk30() throws Exception {
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+ assertHasFullStorageAccess();
+
+ installApp(APK_USES_STORAGE_OPT_OUT_30, null); // opt-out is a no-op on 30
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testTargetingSdk28LosesAccessViaUpdateToTargetSdk30() throws Exception {
+ installApp(APK_USES_STORAGE_DEFAULT_28, null);
+ assertHasFullStorageAccess();
+
+ installApp(APK_USES_STORAGE_OPT_OUT_30, null); // opt-out is a no-op on 30
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testCannotControlStorageWhitelistPostInstall1() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_28, null /*whitelistedPermissions*/);
+
+ // Check expected state of restricted permissions.
+ assertCannotUnWhitelistStorage();
+ }
+
+ @Test
+ @AppModeFull
+ public void testCannotControlStorageWhitelistPostInstall2() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
+
+ // Check expected state of restricted permissions.
+ assertCannotWhitelistStorage();
+ }
+
+ @Test
+ @AppModeFull
+ public void cannotGrantStorageTargetingSdk22NotWhitelisted() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_22, Collections.emptySet());
+
+ eventually(() -> {
+ // Could not grant permission+app-op as targetSDK<29 and not whitelisted
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse();
+
+ // Permissions are always granted for pre-23 apps
+ assertThat(isPermissionGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE))
+ .isTrue();
+ });
+ }
+
+ @Test
+ @AppModeFull
+ public void cannotGrantStorageTargetingSdk22OptInNotWhitelisted() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_IN_22, Collections.emptySet());
+
+ eventually(() -> {
+ // Could not grant permission as targetSDK<29 and not whitelisted
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse();
+
+ // Permissions are always granted for pre-23 apps
+ assertThat(isPermissionGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE))
+ .isTrue();
+ });
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk22Whitelisted() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_22, null);
+
+ // Could grant permission
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk22OptInWhitelisted() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_IN_22, null);
+
+ // Could grant permission
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void cannotGrantStorageTargetingSdk28NotWhitelisted() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_28, Collections.emptySet());
+
+ // Could not grant permission as targetSDK<29 and not whitelisted
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse());
+ }
+
+ @Test
+ @AppModeFull
+ public void cannotGrantStorageTargetingSdk28OptInNotWhitelisted() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_IN_28, Collections.emptySet());
+
+ // Could not grant permission as targetSDK<29 and not whitelisted
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isFalse());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk28Whitelisted() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_28, null);
+
+ // Could grant permission
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk28OptInWhitelisted() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_IN_28, null);
+
+ // Could grant permission
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk29NotWhitelisted() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_29, Collections.emptySet());
+
+ // Could grant permission as targetSDK=29 apps can always grant
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk29OptOutNotWhitelisted() throws Exception {
+ // Install with no whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_OUT_29, Collections.emptySet());
+
+ // Could grant permission as targetSDK=29 apps can always grant
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk29Whitelisted() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_DEFAULT_29, null);
+
+ // Could grant permission as targetSDK=29 apps can always grant
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void canGrantStorageTargetingSdk29OptOutWhitelisted() throws Exception {
+ // Install with whitelisted permissions.
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+
+ // Could grant permission as targetSDK=29 apps can always grant
+ eventually(() ->
+ assertThat(isGranted(PKG, Manifest.permission.READ_EXTERNAL_STORAGE)).isTrue());
+ }
+
+ @Test
+ @AppModeFull
+ public void restrictedWritePermDoesNotImplyIsolatedStorageAccess() throws Exception {
+ // Install with whitelisted read permissions.
+ installApp(
+ APK_USES_STORAGE_OPT_OUT_29,
+ Collections.singleton(Manifest.permission.READ_EXTERNAL_STORAGE));
+
+ // It does not matter that write is restricted as the storage access level is only
+ // controlled by the read perm
+ assertHasFullStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void whitelistedWritePermDoesNotImplyFullStorageAccess() throws Exception {
+ // Install with whitelisted read permissions.
+ installApp(
+ APK_USES_STORAGE_OPT_OUT_29,
+ Collections.singleton(Manifest.permission.WRITE_EXTERNAL_STORAGE));
+
+ // It does not matter that write is white listed as the storage access level is only
+ // controlled by the read perm
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testStorageTargetingSdk30CanPreserveLegacyOnUpdateFromLegacy() throws Exception {
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+ assertHasFullStorageAccess();
+
+ // Updating with the flag preserves legacy
+ installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
+ assertHasFullStorageAccess();
+
+ // And with the flag still preserves legacy
+ installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
+ assertHasFullStorageAccess();
+
+ // But without the flag loses legacy
+ installApp(APK_USES_STORAGE_OPT_OUT_30, null);
+ assertHasIsolatedStorageAccess();
+
+ // And again with the flag doesn't bring back legacy
+ installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testStorageTargetingSdk30CannotPreserveLegacyAfterLegacyUninstall()
+ throws Exception {
+ installApp(APK_USES_STORAGE_OPT_OUT_29, null);
+ assertHasFullStorageAccess();
+
+ runShellCommand("pm uninstall " + PKG);
+
+ installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testStorageTargetingSdk30CannotPreserveLegacyOnUpdateFromNonLegacy()
+ throws Exception {
+ installApp(APK_USES_STORAGE_DEFAULT_29, null);
+ installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
+
+ assertHasIsolatedStorageAccess();
+ }
+
+ @Test
+ @AppModeFull
+ public void testStorageTargetingSdk30CannotPreserveLegacyOnInstall() throws Exception {
+ installApp(APK_USES_STORAGE_PRESERVED_OPT_OUT_30, null);
+
+ assertHasIsolatedStorageAccess();
+ }
+
+ private void assertHasFullStorageAccess() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ final int uid = getContext().getPackageManager().getPackageUid(PKG, 0);
+ eventually(() -> assertThat(appOpsManager.unsafeCheckOpRawNoThrow(
+ AppOpsManager.OPSTR_LEGACY_STORAGE,
+ uid, PKG)).isEqualTo(AppOpsManager.MODE_ALLOWED));
+ });
+ }
+
+ private void assertHasIsolatedStorageAccess() throws Exception {
+ runWithShellPermissionIdentity(() -> {
+ AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+ final int uid = getContext().getPackageManager().getPackageUid(PKG, 0);
+ eventually(() -> assertThat(appOpsManager.unsafeCheckOpRawNoThrow(
+ AppOpsManager.OPSTR_LEGACY_STORAGE,
+ uid, PKG)).isNotEqualTo(AppOpsManager.MODE_ALLOWED));
+ });
+ }
+
+ private void assertCannotWhitelistStorage() throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+
+ runWithShellPermissionIdentity(() -> {
+ // Assert added only to none whitelist.
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
+ | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .doesNotContain(permission.READ_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
+ | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
+ });
+
+ // Assert we cannot add.
+ try {
+ packageManager.addWhitelistedRestrictedPermission(
+ PKG,
+ permission.READ_EXTERNAL_STORAGE,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ packageManager.addWhitelistedRestrictedPermission(
+ PKG,
+ permission.WRITE_EXTERNAL_STORAGE,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ runWithShellPermissionIdentity(() -> {
+ // Assert added only to none whitelist.
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
+ | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .doesNotContain(permission.READ_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
+ | PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
+ });
+ }
+
+ private void assertCannotUnWhitelistStorage() throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+
+ runWithShellPermissionIdentity(() -> {
+ // Assert added only to install whitelist.
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .contains(permission.READ_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .contains(permission.WRITE_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
+ .doesNotContain(permission.READ_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
+ .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
+ });
+
+ try {
+ // Assert we cannot remove.
+ packageManager.removeWhitelistedRestrictedPermission(
+ PKG,
+ permission.READ_EXTERNAL_STORAGE,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ packageManager.removeWhitelistedRestrictedPermission(
+ PKG,
+ permission.WRITE_EXTERNAL_STORAGE,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ runWithShellPermissionIdentity(() -> {
+ // Assert added only to install whitelist.
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .contains(permission.READ_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER))
+ .contains(permission.WRITE_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
+ .doesNotContain(permission.READ_EXTERNAL_STORAGE);
+ assertThat(packageManager.getWhitelistedRestrictedPermissions(PKG,
+ PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
+ | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM))
+ .doesNotContain(permission.WRITE_EXTERNAL_STORAGE);
+ });
+ }
+
+ private @NonNull Set<String> getPermissionsOfAppWithAnyOfFlags(int flags) throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final Set<String> restrictedPermissions = new ArraySet<>();
+ for (String permission : getRequestedPermissionsOfApp()) {
+ PermissionInfo permInfo = packageManager.getPermissionInfo(permission, 0);
+
+ if ((permInfo.flags & flags) != 0) {
+ restrictedPermissions.add(permission);
+ }
+ }
+ return restrictedPermissions;
+ }
+
+ private @NonNull Set<String> getRestrictedPermissionsOfApp() throws Exception {
+ return getPermissionsOfAppWithAnyOfFlags(
+ PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED);
+ }
+
+ private @NonNull String[] getRequestedPermissionsOfApp() throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final PackageInfo packageInfo =
+ packageManager.getPackageInfo(PKG, PackageManager.GET_PERMISSIONS);
+ return packageInfo.requestedPermissions;
+ }
+
+ private static @NonNull Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
+ private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable command)
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity();
+ try {
+ command.run();
+ } finally {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ }
+
+ /**
+ * Install an app.
+ *
+ * @param app The app to be installed
+ * @param whitelistedPermissions The permission to be whitelisted. {@code null} == all
+ * @param grantedPermissions The permission to be granted. {@code null} == all
+ */
+ private void installApp(
+ @NonNull String app,
+ @Nullable Set<String> whitelistedPermissions)
+ throws Exception {
+ String bypassLowTargetSdkFlag = "";
+ if (SdkLevel.isAtLeastU()) {
+ bypassLowTargetSdkFlag = " --bypass-low-target-sdk-block";
+ }
+
+ // Install the app and whitelist/grant all permission if requested.
+ String installResult = runShellCommandOrThrow("pm install"
+ + bypassLowTargetSdkFlag + " -t -r --restrict-permissions " + app);
+ assertThat(installResult.trim()).isEqualTo("Success");
+
+ final Set<String> adjustedWhitelistedPermissions;
+ if (whitelistedPermissions == null) {
+ adjustedWhitelistedPermissions = getRestrictedPermissionsOfApp();
+ } else {
+ adjustedWhitelistedPermissions = whitelistedPermissions;
+ }
+
+ final Set<String> adjustedGrantedPermissions = getRestrictedPermissionsOfApp();
+
+ // Whitelist subset of permissions if requested
+ runWithShellPermissionIdentity(() -> {
+ final PackageManager packageManager = getContext().getPackageManager();
+ for (String permission : adjustedWhitelistedPermissions) {
+ packageManager.addWhitelistedRestrictedPermission(
+ PKG,
+ permission,
+ PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
+ }
+ });
+
+ // Grant subset of permissions if requested
+ runWithShellPermissionIdentity(() -> {
+ final PackageManager packageManager = getContext().getPackageManager();
+ for (String permission : adjustedGrantedPermissions) {
+ packageManager.grantRuntimePermission(PKG, permission, getContext().getUser());
+ packageManager.updatePermissionFlags(
+ permission,
+ PKG,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ 0,
+ getContext().getUser());
+ }
+ });
+
+ // Mark all permissions as reviewed as for pre-22 apps the restriction state might not be
+ // applied until reviewed
+ runWithShellPermissionIdentity(() -> {
+ final PackageManager packageManager = getContext().getPackageManager();
+ for (String permission : getRequestedPermissionsOfApp()) {
+ packageManager.updatePermissionFlags(
+ permission,
+ PKG,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
+ 0,
+ getContext().getUser());
+ }
+ });
+ }
+
+ @After
+ public void uninstallApp() {
+ runShellCommand("pm uninstall " + PKG);
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt
new file mode 100644
index 000000000..6b3ae5f2e
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/RuntimePermissionProperties.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts
+
+import android.Manifest.permission.ACCEPT_HANDOVER
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.Manifest.permission.ACTIVITY_RECOGNITION
+import android.Manifest.permission.ADD_VOICEMAIL
+import android.Manifest.permission.ANSWER_PHONE_CALLS
+import android.Manifest.permission.BLUETOOTH_ADVERTISE
+import android.Manifest.permission.BLUETOOTH_CONNECT
+import android.Manifest.permission.BLUETOOTH_SCAN
+import android.Manifest.permission.BODY_SENSORS
+import android.Manifest.permission.CALL_PHONE
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.GET_ACCOUNTS
+import android.Manifest.permission.NEARBY_WIFI_DEVICES
+import android.Manifest.permission.PACKAGE_USAGE_STATS
+import android.Manifest.permission.POST_NOTIFICATIONS
+import android.Manifest.permission.PROCESS_OUTGOING_CALLS
+import android.Manifest.permission.READ_CALENDAR
+import android.Manifest.permission.READ_CALL_LOG
+import android.Manifest.permission.READ_CELL_BROADCASTS
+import android.Manifest.permission.READ_CONTACTS
+import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED
+import android.Manifest.permission.READ_PHONE_NUMBERS
+import android.Manifest.permission.READ_PHONE_STATE
+import android.Manifest.permission.READ_SMS
+import android.Manifest.permission.RECEIVE_MMS
+import android.Manifest.permission.RECEIVE_SMS
+import android.Manifest.permission.RECEIVE_WAP_PUSH
+import android.Manifest.permission.RECORD_AUDIO
+import android.Manifest.permission.SEND_SMS
+import android.Manifest.permission.USE_SIP
+import android.Manifest.permission.UWB_RANGING
+import android.Manifest.permission.WRITE_CALENDAR
+import android.Manifest.permission.WRITE_CALL_LOG
+import android.Manifest.permission.WRITE_CONTACTS
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+import android.Manifest.permission_group.UNDEFINED
+import android.app.AppOpsManager.permissionToOp
+import android.content.pm.PackageManager.GET_PERMISSIONS
+import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS
+import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP
+import android.os.Build
+import android.permission.PermissionManager
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RuntimePermissionProperties {
+ private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ private val pm = context.packageManager
+
+ private val platformPkg = pm.getPackageInfo("android", GET_PERMISSIONS)
+ private val platformRuntimePerms =
+ platformPkg.permissions!!.filter { it.protection == PROTECTION_DANGEROUS }
+ private val platformBgPermNames = platformRuntimePerms.mapNotNull { it.backgroundPermission }
+
+ @Test
+ fun allRuntimeForegroundPermissionNeedAnAppOp() {
+ val platformFgPerms = platformRuntimePerms.filter { !platformBgPermNames.contains(it.name) }
+
+ for (perm in platformFgPerms) {
+ assertWithMessage("AppOp for ${perm.name}").that(permissionToOp(perm.name)).isNotNull()
+ }
+ }
+
+ @Test
+ fun groupOfRuntimePermissionsShouldBeUnknown() {
+ for (perm in platformRuntimePerms) {
+ assertWithMessage("Group of ${perm.name}").that(perm.group).isEqualTo(UNDEFINED)
+ }
+ }
+
+ @Test
+ fun allAppOpPermissionNeedAnAppOp() {
+ val platformAppOpPerms =
+ platformPkg.permissions!!
+ .filter { (it.protectionFlags and PROTECTION_FLAG_APPOP) != 0 }
+ .filter {
+ // Grandfather incomplete definition of PACKAGE_USAGE_STATS
+ it.name != PACKAGE_USAGE_STATS
+ }
+
+ for (perm in platformAppOpPerms) {
+ assertWithMessage("AppOp for ${perm.name}").that(permissionToOp(perm.name)).isNotNull()
+ }
+ }
+
+ /** The permission of a background permission is the one of its foreground permission */
+ @Test
+ fun allRuntimeBackgroundPermissionCantHaveAnAppOp() {
+ val platformBgPerms = platformRuntimePerms.filter { platformBgPermNames.contains(it.name) }
+
+ for (perm in platformBgPerms) {
+ assertWithMessage("AppOp for ${perm.name}").that(permissionToOp(perm.name)).isNull()
+ }
+ }
+
+ /** Commonly a new runtime permission is created by splitting an old one into twice */
+ @Test
+ fun runtimePermissionsShouldHaveBeenSplitFromPreviousPermission() {
+ // Runtime permissions in Android P
+ val expectedPerms =
+ mutableSetOf(
+ READ_CONTACTS,
+ WRITE_CONTACTS,
+ GET_ACCOUNTS,
+ READ_CALENDAR,
+ WRITE_CALENDAR,
+ SEND_SMS,
+ RECEIVE_SMS,
+ READ_SMS,
+ RECEIVE_MMS,
+ RECEIVE_WAP_PUSH,
+ READ_CELL_BROADCASTS,
+ READ_EXTERNAL_STORAGE,
+ WRITE_EXTERNAL_STORAGE,
+ ACCESS_FINE_LOCATION,
+ ACCESS_COARSE_LOCATION,
+ READ_CALL_LOG,
+ WRITE_CALL_LOG,
+ PROCESS_OUTGOING_CALLS,
+ READ_PHONE_STATE,
+ READ_PHONE_NUMBERS,
+ CALL_PHONE,
+ ADD_VOICEMAIL,
+ USE_SIP,
+ ANSWER_PHONE_CALLS,
+ ACCEPT_HANDOVER,
+ RECORD_AUDIO,
+ CAMERA,
+ BODY_SENSORS
+ )
+
+ // Add permission split since P
+ for (sdkVersion in Build.VERSION_CODES.P + 1..Build.VERSION_CODES.CUR_DEVELOPMENT + 1) {
+ for (splitPerm in
+ context.getSystemService(PermissionManager::class.java)!!.splitPermissions) {
+ if (
+ splitPerm.targetSdk == sdkVersion &&
+ expectedPerms.contains(splitPerm.splitPermission)
+ ) {
+ expectedPerms.addAll(splitPerm.newPermissions)
+ }
+ }
+ }
+
+ // Add runtime permission added in Q which were _not_ split from a previously existing
+ // runtime permission
+ expectedPerms.add(ACTIVITY_RECOGNITION)
+
+ // Add runtime permissions added in S which were _not_ split from a previously existing
+ // runtime permission
+ expectedPerms.add(BLUETOOTH_ADVERTISE)
+ expectedPerms.add(BLUETOOTH_CONNECT)
+ expectedPerms.add(BLUETOOTH_SCAN)
+ expectedPerms.add(UWB_RANGING)
+
+ // Add runtime permissions added in T which were _not_ split from a previously existing
+ // runtime permission
+ expectedPerms.add(POST_NOTIFICATIONS)
+ expectedPerms.add(NEARBY_WIFI_DEVICES)
+
+ // Add runtime permissions added in U which were _not_ split from a previously existing
+ // runtime permission
+ expectedPerms.add(READ_MEDIA_VISUAL_USER_SELECTED)
+
+ assertThat(expectedPerms).containsExactlyElementsIn(platformRuntimePerms.map { it.name })
+ }
+}
diff --git a/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt
new file mode 100644
index 000000000..268bbd317
--- /dev/null
+++ b/tests/cts/permissionpolicy/src/android/permissionpolicy/cts/SignaturePermissionAllowlistConfigTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionpolicy.cts
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.PermissionInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.CddTest
+import com.android.compatibility.common.util.PermissionUtils
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SignaturePermissionAllowlistConfigTest {
+ private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ private val packageManager = context.packageManager
+
+ @CddTest(requirements = arrayOf("9.1/C-0-16"))
+ @Test
+ fun allPlatformSignedNonSystemPackagesHavePlatformSignaturePermissionsAllowlisted() {
+ val allowlist = getSignaturePermissionAllowlist()
+ val platformSignaturePermissionNames = getPlatformSignaturePermissionNames()
+ val unallowlistedPackageAndPermissions = mutableMapOf<String, MutableList<String>>()
+ for (packageInfo in getPlatformSignedNonSystemPackageInfos()) {
+ val permissionNames = allowlist[packageInfo.packageName]
+ packageInfo.requestedPermissions?.forEach { permissionName ->
+ if (permissionName in platformSignaturePermissionNames) {
+ if (permissionNames?.contains(permissionName) != true) {
+ unallowlistedPackageAndPermissions.getOrPut(packageInfo.packageName) {
+ mutableListOf()
+ } += permissionName
+ }
+ }
+ }
+ }
+ assertTrue(
+ "Some platform-signed non-system packages don't have their requested platform" +
+ " signature permissions allowlisted. Suggested signature permission allowlist" +
+ " additions:\n\n" +
+ buildSiganturePermissionAllowlist(unallowlistedPackageAndPermissions),
+ unallowlistedPackageAndPermissions.isEmpty()
+ )
+ }
+
+ private fun getSignaturePermissionAllowlist(): Map<String, Set<String>> {
+ val allowlist = mutableMapOf<String, MutableSet<String>>()
+ val partitions = listOf("system", "system-ext", "vendor", "product", "apex")
+ for (partition in partitions) {
+ val output =
+ SystemUtil.runShellCommandOrThrow(
+ "pm get-signature-permission-allowlist $partition"
+ )
+ .trim()
+ lateinit var permissionNames: MutableSet<String>
+ for (line in output.split("\n")) {
+ val line = line.trim()
+ when {
+ line.startsWith("Package: ") -> {
+ val packageName = line.substring("Package: ".length)
+ permissionNames = allowlist.getOrPut(packageName) { mutableSetOf() }
+ }
+ line.startsWith("Permission: ") -> {
+ val permissionName = line.substring("Permission: ".length)
+ permissionNames += permissionName
+ }
+ line.isEmpty() -> {}
+ else -> error("Unknown line in pm get-signature-permission-allowlist: $line")
+ }
+ }
+ }
+ return allowlist
+ }
+
+ @CddTest(requirements = arrayOf("9.1/C-0-16"))
+ @Test
+ fun allPlatformSignedNonSystemPackagesHavePlatformSignaturePermissionsGranted() {
+ val platformSignaturePermissionNames = getPlatformSignaturePermissionNames()
+ val deniedPackageAndPermissions = mutableMapOf<String, MutableList<String>>()
+ for (packageInfo in getPlatformSignedNonSystemPackageInfos()) {
+ packageInfo.requestedPermissions?.forEachIndexed { index, permissionName ->
+ val permissionFlags = packageInfo.requestedPermissionsFlags!![index]
+ if (permissionName in platformSignaturePermissionNames) {
+ if (permissionFlags and PackageInfo.REQUESTED_PERMISSION_GRANTED == 0) {
+ deniedPackageAndPermissions.getOrPut(packageInfo.packageName) {
+ mutableListOf()
+ } += permissionName
+ }
+ }
+ }
+ }
+ assertTrue(
+ "Some platform-signed non-system packages don't have their requested platform" +
+ " signature permissions granted. Suggested signature permission allowlist" +
+ " additions:\n\n${buildSiganturePermissionAllowlist(deniedPackageAndPermissions)}",
+ deniedPackageAndPermissions.isEmpty()
+ )
+ }
+
+ private fun getPlatformSignaturePermissionNames(): List<String> =
+ packageManager
+ .getPackageInfo("android", PackageManager.GET_PERMISSIONS)
+ .permissions!!
+ .filter { it.protection == PermissionInfo.PROTECTION_SIGNATURE }
+ .map { it.name }
+
+ private fun getPlatformSignedNonSystemPackageInfos(): List<PackageInfo> =
+ packageManager
+ .getInstalledPackages(PackageManager.GET_PERMISSIONS)
+ .filter {
+ it.applicationInfo!!.flags and ApplicationInfo.FLAG_SYSTEM == 0 ||
+ it.applicationInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP != 0
+ }
+ .filter { PermissionUtils.isPlatformSigned(it.packageName) }
+
+ private fun buildSiganturePermissionAllowlist(
+ packagesAndPermissions: Map<String, List<String>>
+ ): String = buildString {
+ for ((packageName, permissionNames) in packagesAndPermissions) {
+ append(" <signature-permissions package=\"$packageName\">\n")
+ for (permissionName in permissionNames) {
+ append(" <permission name=\"$permissionName\" />\n")
+ }
+ append(" </signature-permissions>\n")
+ }
+ }
+}
diff --git a/tests/cts/permissionui/Android.bp b/tests/cts/permissionui/Android.bp
new file mode 100644
index 000000000..c1ec017ff
--- /dev/null
+++ b/tests/cts/permissionui/Android.bp
@@ -0,0 +1,87 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "CtsPermissionUiTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.kt",
+ ":CtsMediaProviderTestUtils",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "bluetooth-test-util-lib",
+ "modules-utils-build_system",
+ "androidx.test.core",
+ "permission-test-util-lib",
+ "sts-device-util",
+ "cts-wm-util",
+ "flag-junit",
+ "CtsAccessibilityCommon",
+ "platform-test-rules",
+ "platform-test-annotations",
+ "android.content.pm.flags-aconfig-java-export",
+ "android.permission.flags-aconfig-java-export",
+ "Harrier",
+ ],
+ data: [
+ ":CtsPermissionPolicyApp25",
+ ":CtsUsePermissionApp22",
+ ":CtsUsePermissionApp22CalendarOnly",
+ ":CtsUsePermissionApp22None",
+ ":CtsUsePermissionApp23",
+ ":CtsUsePermissionApp25",
+ ":CtsUsePermissionApp26",
+ ":CtsUsePermissionApp28",
+ ":CtsUsePermissionApp29",
+ ":CtsUsePermissionApp30",
+ ":CtsUsePermissionApp30WithBackground",
+ ":CtsUsePermissionApp30WithBluetooth",
+ ":CtsUsePermissionApp31",
+ ":CtsUsePermissionApp31WithAsl",
+ ":CtsUsePermissionApp32",
+ ":CtsUsePermissionAppLatest",
+ ":CtsUsePermissionAppLatestNone",
+ ":CtsUsePermissionAppWithOverlay",
+ ":CtsAccessMicrophoneAppLocationProvider",
+ ":CtsHelperAppOverlay",
+ ":CtsCreateNotificationChannelsApp31",
+ ":CtsMediaPermissionApp33WithStorage",
+ ":CtsDifferentPkgNameApp",
+ ":CtsUsePermissionAppImplicitUserSelectStorage",
+ ":CtsAppThatAccessesMicAndCameraPermission",
+ ":CtsUsePermissionAppStorage33",
+ ":CtsAppThatMakesTwoPermRequests",
+ ],
+ test_suites: [
+ "cts",
+ "sts",
+ "general-tests",
+ "mts-permission",
+ "automotive-tests",
+ "automotive-general-tests",
+ "mcts-permission",
+ ],
+}
diff --git a/tests/cts/permissionui/AndroidManifest.xml b/tests/cts/permissionui/AndroidManifest.xml
new file mode 100644
index 000000000..3b80b8d8b
--- /dev/null
+++ b/tests/cts/permissionui/AndroidManifest.xml
@@ -0,0 +1,89 @@
+<?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="android.permissionui.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.GET_TASKS" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+
+ <application>
+
+ <uses-library android:name="android.test.runner" />
+
+ <service android:name="android.permission.cts.CtsNotificationListenerService"
+ android:exported="true"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService"/>
+ </intent-filter>
+ </service>
+
+ <activity android:name="com.android.compatibility.common.util.FutureResultActivity" />
+ <activity
+ android:name=".StartForFutureActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"/>
+
+ <activity android:name=".TestInstallerActivity"
+ android:exported="true"
+ android:enabled="false"
+ android:launchMode="singleInstance">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.SHOW_APP_INFO" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <service android:name=".AccessibilityTestService1"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:label="@string/test_accessibility_service"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService"/>
+ </intent-filter>
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/test_accessibilityservice"/>
+ </service>
+
+ <service android:name=".AccessibilityTestService2"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:label="@string/test_accessibility_service_2"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService"/>
+ </intent-filter>
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/test_accessibilityservice"/>
+ </service>
+
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.permissionui.cts"
+ android:label="CTS UI tests for permissions">
+ </instrumentation>
+</manifest>
diff --git a/tests/cts/permissionui/AndroidTest.xml b/tests/cts/permissionui/AndroidTest.xml
new file mode 100644
index 000000000..a44909206
--- /dev/null
+++ b/tests/cts/permissionui/AndroidTest.xml
@@ -0,0 +1,104 @@
+<?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.
+ -->
+
+<configuration description="Config for CTS Permission UI test cases">
+
+ <option name="test-suite-tag" value="cts" />
+
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <!-- Keep screen on for Bluetooth scanning -->
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ <option name="disable-device-config-sync" value="true" />
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPermissionUiTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="CtsAccessMicrophoneAppLocationProvider.apk->/data/local/tmp/cts-permissionui/CtsAccessMicrophoneAppLocationProvider.apk" />
+ <option name="push" value="CtsPermissionPolicyApp25.apk->/data/local/tmp/cts-permissionui/CtsPermissionPolicyApp25.apk" />
+ <option name="push" value="CtsUsePermissionApp22.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp22.apk" />
+ <option name="push" value="CtsUsePermissionApp22CalendarOnly.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp22CalendarOnly.apk" />
+ <option name="push" value="CtsUsePermissionApp22None.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp22None.apk" />
+ <option name="push" value="CtsUsePermissionApp23.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp23.apk" />
+ <option name="push" value="CtsUsePermissionApp25.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp25.apk" />
+ <option name="push" value="CtsUsePermissionApp26.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp26.apk" />
+ <option name="push" value="CtsUsePermissionApp28.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp28.apk" />
+ <option name="push" value="CtsUsePermissionApp29.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp29.apk" />
+ <option name="push" value="CtsUsePermissionApp30.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp30.apk" />
+ <option name="push" value="CtsUsePermissionApp30WithBackground.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp30WithBackground.apk" />
+ <option name="push" value="CtsUsePermissionApp30WithBluetooth.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp30WithBluetooth.apk" />
+ <option name="push" value="CtsUsePermissionApp31.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp31.apk" />
+ <option name="push" value="CtsUsePermissionApp31WithAsl.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp31WithAsl.apk" />
+ <option name="push" value="CtsUsePermissionApp32.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionApp32.apk" />
+ <option name="push" value="CtsUsePermissionAppLatest.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppLatest.apk" />
+ <option name="push" value="CtsUsePermissionAppLatestNone.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppLatestNone.apk" />
+ <option name="push" value="CtsUsePermissionAppWithOverlay.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppWithOverlay.apk" />
+ <option name="push" value="CtsHelperAppOverlay.apk->/data/local/tmp/cts-permissionui/CtsHelperAppOverlay.apk" />
+ <option name="push" value="CtsCreateNotificationChannelsApp31.apk->/data/local/tmp/cts-permissionui/CtsCreateNotificationChannelsApp31.apk" />
+ <option name="push" value="CtsDifferentPkgNameApp.apk->/data/local/tmp/cts-permissionui/CtsDifferentPkgNameApp.apk" />
+ <option name="push" value="CtsMediaPermissionApp33WithStorage.apk->/data/local/tmp/cts-permissionui/CtsMediaPermissionApp33WithStorage.apk" />
+ <option name="push" value="CtsUsePermissionAppImplicitUserSelectStorage.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppImplicitUserSelectStorage.apk" />
+ <option name="push" value="CtsAppThatAccessesMicAndCameraPermission.apk->/data/local/tmp/cts-permissionui/CtsAppThatAccessesMicAndCameraPermission.apk" />
+ <option name="push" value="CtsUsePermissionAppStorage33.apk->/data/local/tmp/cts-permissionui/CtsUsePermissionAppStorage33.apk" />
+ <option name="push" value="CtsAppThatMakesTwoPermRequests.apk->/data/local/tmp/cts-permissionui/CtsAppThatMakesTwoPermRequests.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="appops set android.permissionui.cts REQUEST_INSTALL_PACKAGES allow" />
+ <option name="run-command" value="am wait-for-broadcast-barrier" />
+ <!-- ensure device provisioning and user setup are marked as completed -->
+ <option name="run-command" value="settings put global device_provisioned 1" />
+ <option name="run-command" value="settings put secure user_setup_complete 1" />
+ <!-- disable DeprecatedAbi warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <!-- disable DeprecatedTargetSdk warning -->
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1" />
+ </target_preparer>
+
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-permissionui" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-permissionui"/>
+ </target_preparer>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/data/user/0/android.permissionui.cts/files" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.permissionui.cts" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+</configuration>
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/Android.bp b/tests/cts/permissionui/AppThatAccessesCameraAndMic/Android.bp
new file mode 100644
index 000000000..e0d9fd791
--- /dev/null
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsAppThatAccessesMicAndCameraPermission",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "31",
+
+ static_libs: [
+ "androidx.test.rules",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
+ ],
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml b/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml
new file mode 100644
index 000000000..67c691e0a
--- /dev/null
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionui.cts.appthataccessescameraandmic"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <application android:label="CtsCameraMicAccess">
+ <activity android:name=".AccessCameraOrMicActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:theme="@style/AccessCameraOrMicActivityTheme"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="test.action.USE_CAMERA_OR_MIC" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values-watch/styles.xml b/tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values-watch/styles.xml
new file mode 100644
index 000000000..21c73df65
--- /dev/null
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values-watch/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <style name="AccessCameraOrMicActivityTheme" parent="android:style/Theme.DeviceDefault">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">#33FF0000</item>
+ <item name="android:windowDisablePreview">true</item>
+ </style>
+</resources>
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values/styles.xml b/tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values/styles.xml
new file mode 100644
index 000000000..9889e3f0b
--- /dev/null
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/res/values/styles.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <style name="AccessCameraOrMicActivityTheme" parent="android:style/Theme.DeviceDefault" />
+</resources>
diff --git a/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt b/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
new file mode 100644
index 000000000..aaeae4fac
--- /dev/null
+++ b/tests/cts/permissionui/AppThatAccessesCameraAndMic/src/android/permissionui/cts/appthataccessescameraandmic/AccessCameraOrMicActivity.kt
@@ -0,0 +1,275 @@
+/*
+ * 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 android.permissionui.cts.appthataccessescameraandmic
+
+import android.app.Activity
+import android.app.AppOpsManager
+import android.content.pm.PackageManager
+import android.hardware.camera2.CameraAccessException
+import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.params.OutputConfiguration
+import android.hardware.camera2.params.SessionConfiguration
+import android.media.AudioFormat.CHANNEL_IN_MONO
+import android.media.AudioFormat.ENCODING_PCM_16BIT
+import android.media.AudioRecord
+import android.media.ImageReader
+import android.media.MediaRecorder.AudioSource.MIC
+import android.os.Bundle
+import android.os.Handler
+import android.os.Process
+import android.util.Log
+import android.util.Size
+import android.view.WindowManager
+import androidx.annotation.NonNull
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+private const val USE_CAMERA = "use_camera"
+private const val USE_MICROPHONE = "use_microphone"
+private const val USE_HOTWORD = "use_hotword"
+private const val FINISH_EARLY = "finish_early"
+private const val USE_DURATION_MS = 10000L
+private const val SAMPLE_RATE_HZ = 44100
+
+/**
+ * Activity which will, depending on the extra passed in the intent, use the camera, the microphone,
+ * or both.
+ */
+class AccessCameraOrMicActivity : Activity() {
+ private lateinit var cameraManager: CameraManager
+ private lateinit var cameraId: String
+ private var cameraDevice: CameraDevice? = null
+ private var recorder: AudioRecord? = null
+ private var appOpsManager: AppOpsManager? = null
+ private var cameraFinished = false
+ private var runCamera = false
+ private var backupCameraOpRunning = true
+ private var micFinished = false
+ private var runMic = false
+ private var hotwordFinished = false
+ private var runHotword = false
+ private var finishEarly = false
+ private var isWatch = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ throw RuntimeException(
+ "Activity was recreated (perhaps due to a configuration change?) " +
+ "and this activity doesn't currently know how to gracefully handle " +
+ "configuration changes."
+ )
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ runCamera = intent.getBooleanExtra(USE_CAMERA, false)
+ runMic = intent.getBooleanExtra(USE_MICROPHONE, false)
+ runHotword = intent.getBooleanExtra(USE_HOTWORD, false)
+ finishEarly = intent.getBooleanExtra(FINISH_EARLY, false)
+ isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+
+ if (runMic) {
+ useMic()
+ }
+
+ if (runCamera) {
+ useCamera()
+ }
+
+ if (runHotword) {
+ useHotword()
+ }
+
+ if (isWatch) {
+ // Make it possible for uiautomator to find the microphone icon
+ // The icon is shown on the home screen so it is hidden behind the activity unless the
+ // activity is set to translucent.
+ getWindow()
+ .setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ )
+ getWindow().setLayout(100, 100)
+ }
+ }
+
+ override fun finish() {
+ super.finish()
+ cameraDevice?.close()
+ cameraDevice = null
+ recorder?.stop()
+ recorder = null
+ if (runCamera) {
+ appOpsManager?.finishOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(), packageName)
+ }
+ if (runHotword) {
+ appOpsManager?.finishOp(
+ AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ Process.myUid(),
+ packageName
+ )
+ }
+ appOpsManager = null
+ }
+
+ override fun onStop() {
+ super.onStop()
+ finish()
+ }
+
+ private val stateCallback =
+ object : CameraDevice.StateCallback() {
+ override fun onOpened(@NonNull camDevice: CameraDevice) {
+ cameraDevice = camDevice
+ val config =
+ cameraManager!!
+ .getCameraCharacteristics(cameraId)
+ .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+ val outputFormat = config!!.outputFormats[0]
+ val outputSize: Size = config!!.getOutputSizes(outputFormat)[0]
+ val handler = Handler(mainLooper)
+
+ val imageReader =
+ ImageReader.newInstance(outputSize.width, outputSize.height, outputFormat, 2)
+
+ val builder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
+ builder.addTarget(imageReader.surface)
+ val captureRequest = builder.build()
+ val sessionConfiguration =
+ SessionConfiguration(
+ SessionConfiguration.SESSION_REGULAR,
+ listOf(OutputConfiguration(imageReader.surface)),
+ mainExecutor,
+ object : CameraCaptureSession.StateCallback() {
+ override fun onConfigured(session: CameraCaptureSession) {
+ session.capture(captureRequest, null, handler)
+ }
+
+ override fun onConfigureFailed(session: CameraCaptureSession) {}
+
+ override fun onReady(session: CameraCaptureSession) {}
+ }
+ )
+
+ imageReader.setOnImageAvailableListener(
+ {
+ GlobalScope.launch {
+ delay(USE_DURATION_MS)
+ if (!backupCameraOpRunning) {
+ cameraFinished = true
+ if (!runMic || micFinished) {
+ finish()
+ }
+ }
+ }
+ },
+ handler
+ )
+ cameraDevice!!.createCaptureSession(sessionConfiguration)
+ }
+
+ override fun onDisconnected(@NonNull camDevice: CameraDevice) {
+ Log.e("CameraMicIndicatorsPermissionTest", "camera disconnected")
+ startBackupCamera(camDevice)
+ }
+
+ override fun onError(@NonNull camDevice: CameraDevice, error: Int) {
+ Log.e("CameraMicIndicatorsPermissionTest", "camera error $error")
+ startBackupCamera(camDevice)
+ }
+ }
+
+ private fun startBackupCamera(camDevice: CameraDevice?) {
+ // Something went wrong with the camera. Fallback to direct app op usage
+ if (runCamera && !cameraFinished) {
+ backupCameraOpRunning = true
+ appOpsManager = getSystemService(AppOpsManager::class.java)
+ appOpsManager?.startOpNoThrow(AppOpsManager.OPSTR_CAMERA, Process.myUid(), packageName)
+
+ GlobalScope.launch {
+ delay(USE_DURATION_MS)
+ cameraFinished = true
+ backupCameraOpRunning = false
+ finishIfAllDone()
+ }
+ }
+ camDevice?.close()
+ if (camDevice == cameraDevice) {
+ cameraDevice = null
+ }
+ }
+
+ @Throws(CameraAccessException::class)
+ private fun useCamera() {
+ // TODO 192690992: determine why the camera manager code is flaky
+ startBackupCamera(null)
+ /*
+ cameraManager = getSystemService(CameraManager::class.java)!!
+ cameraId = cameraManager.cameraIdList[0]
+ cameraManager.openCamera(cameraId, mainExecutor, stateCallback)
+ */
+ }
+
+ private fun useMic() {
+ val minSize =
+ AudioRecord.getMinBufferSize(SAMPLE_RATE_HZ, CHANNEL_IN_MONO, ENCODING_PCM_16BIT)
+ recorder = AudioRecord(MIC, SAMPLE_RATE_HZ, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, minSize)
+ recorder?.startRecording()
+ if (finishEarly) {
+ appOpsManager = getSystemService(AppOpsManager::class.java)
+ appOpsManager?.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO, Process.myUid(), packageName)
+ return
+ }
+ GlobalScope.launch {
+ delay(USE_DURATION_MS)
+ micFinished = true
+ finishIfAllDone()
+ }
+ }
+
+ private fun useHotword() {
+ appOpsManager = getSystemService(AppOpsManager::class.java)
+ appOpsManager?.startOpNoThrow(
+ AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ Process.myUid(),
+ packageName
+ )
+
+ GlobalScope.launch {
+ delay(USE_DURATION_MS)
+ hotwordFinished = true
+ finishIfAllDone()
+ }
+ }
+
+ private fun finishIfAllDone() {
+ if (
+ (!runMic || micFinished) &&
+ (!runCamera || cameraFinished) &&
+ (!runHotword || hotwordFinished)
+ ) {
+ finish()
+ }
+ }
+}
diff --git a/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/Android.bp b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/Android.bp
new file mode 100644
index 000000000..c2ebe22b1
--- /dev/null
+++ b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsAppThatMakesTwoPermRequests",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "29",
+
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
diff --git a/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/AndroidManifest.xml b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/AndroidManifest.xml
new file mode 100644
index 000000000..f255f2fed
--- /dev/null
+++ b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.permissionui.cts.usepermission"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+
+ <application android:label="StartTwoPermissionRequests">
+ <activity android:name=".Activity1"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".Activity2" android:exported="true"/>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity1.kt b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity1.kt
new file mode 100644
index 000000000..b806496e4
--- /dev/null
+++ b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity1.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.Manifest.permission.READ_CONTACTS
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.os.Handler
+
+class Activity1 : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ requestPermissions(arrayOf(READ_CONTACTS), 2)
+ }
+
+ // Once the permissions dialog is showing over this activity, trigger the next dialog, and
+ // finish
+ override fun onPause() {
+ super.onPause()
+ finish()
+ Handler().postDelayed({ startActivity(Intent(this, Activity2::class.java)) }, 1000)
+ }
+}
diff --git a/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity2.kt b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity2.kt
new file mode 100644
index 000000000..15d40c324
--- /dev/null
+++ b/tests/cts/permissionui/AppThatStartsTwoPermissionDialogs/src/android/permissionui/cts/usepermission/Activity2.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.Manifest
+import android.app.Activity
+import android.os.Bundle
+import android.os.Handler
+
+class Activity2 : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ requestPermissions(arrayOf(Manifest.permission.CALL_PHONE), 3)
+ Handler().postDelayed({ finish() }, 2000)
+ }
+}
diff --git a/tests/cts/permissionui/CreateNotificationChannelsApp31/Android.bp b/tests/cts/permissionui/CreateNotificationChannelsApp31/Android.bp
new file mode 100644
index 000000000..265a01c69
--- /dev/null
+++ b/tests/cts/permissionui/CreateNotificationChannelsApp31/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsCreateNotificationChannelsApp31",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "31",
+
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
diff --git a/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml b/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml
new file mode 100644
index 000000000..b342319f3
--- /dev/null
+++ b/tests/cts/permissionui/CreateNotificationChannelsApp31/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.permissionui.cts.usepermission"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="31" android:targetSdkVersion="31" />
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+ <application android:label="CreateNotif">
+ <activity android:name=".CreateNotificationChannelsActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="usepermission.createchannels.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt b/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt
new file mode 100644
index 000000000..302d021ca
--- /dev/null
+++ b/tests/cts/permissionui/CreateNotificationChannelsApp31/src/android/permissionui/cts/usepermission/CreateNotificationChannelsActivity.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.Manifest
+import android.app.Activity
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+
+const val EXTRA_CREATE_CHANNELS = "extra_create"
+const val EXTRA_REQUEST_NOTIF_PERMISSION = "extra_request_notif_permission"
+const val EXTRA_REQUEST_OTHER_PERMISSIONS = "extra_request_permissions"
+const val EXTRA_START_SECOND_ACTIVITY = "extra_start_second_activity"
+const val EXTRA_START_SECOND_APP = "extra_start_second_app"
+const val SECONDARY_APP_INTENT = "emptyactivity.main"
+const val SECONDARY_APP_PKG = "android.permissionui.cts.usepermissionother"
+const val TEST_PKG = "android.permissionui.cts"
+const val CHANNEL_ID_31 = "test_channel_id"
+const val BROADCAST_ACTION = "usepermission.createchannels.BROADCAST"
+const val DELAY_MS = 1000L
+const val LONG_DELAY_MS = 2000L
+
+class CreateNotificationChannelsActivity : Activity() {
+ private lateinit var notificationManager: NotificationManager
+ private var launchActivityOnSecondResume = false
+ private var isFirstResume = true
+ private var windowHasFocus = false
+ private var pendingCreateChannel = false
+ private val handler = Handler(Looper.getMainLooper())
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ throw RuntimeException(
+ "Activity was recreated (perhaps due to a configuration change?) " +
+ "and this activity doesn't currently know how to gracefully handle " +
+ "configuration changes."
+ )
+ }
+
+ registerReceiver(receiver, IntentFilter(BROADCAST_ACTION), RECEIVER_EXPORTED)
+ handleIntent(intent)
+ }
+
+ private fun handleIntent(providedIntent: Intent?, broacastAfterComplete: Boolean = false) {
+ if (providedIntent == null) {
+ return
+ }
+ val launchSecondActivity =
+ providedIntent.getBooleanExtra(EXTRA_START_SECOND_ACTIVITY, false)
+ notificationManager = baseContext.getSystemService(NotificationManager::class.java)!!
+ if (providedIntent.getBooleanExtra(EXTRA_START_SECOND_APP, false)) {
+ handler.postDelayed(
+ {
+ val intent2 = Intent(SECONDARY_APP_INTENT)
+ intent2.`package` = SECONDARY_APP_PKG
+ intent2.addCategory(Intent.CATEGORY_DEFAULT)
+ handler.postDelayed({ createChannel() }, DELAY_MS)
+ startActivity(intent2)
+ },
+ LONG_DELAY_MS
+ )
+ } else if (providedIntent.getBooleanExtra(EXTRA_CREATE_CHANNELS, false)) {
+ createChannel()
+ if (launchSecondActivity) {
+ launchActivityOnSecondResume = true
+ }
+ } else if (launchSecondActivity) {
+ launchSecondActivity()
+ }
+
+ if (providedIntent.getBooleanExtra(EXTRA_REQUEST_OTHER_PERMISSIONS, false)) {
+ requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), 0)
+ }
+
+ if (providedIntent.getBooleanExtra(EXTRA_REQUEST_NOTIF_PERMISSION, false)) {
+ requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
+ }
+
+ if (broacastAfterComplete) {
+ sendBroadcast(Intent(BROADCAST_ACTION).setPackage(TEST_PKG))
+ }
+ }
+
+ private val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ handleIntent(intent, true)
+ }
+ }
+
+ private fun launchSecondActivity() {
+ handler.postDelayed(
+ {
+ val intent2 = Intent(Intent.ACTION_MAIN)
+ intent2.`package` = packageName
+ intent2.addCategory(Intent.CATEGORY_DEFAULT)
+ intent2.putExtra(EXTRA_CREATE_CHANNELS, true)
+ startActivity(intent2)
+ },
+ LONG_DELAY_MS
+ )
+ }
+
+ override fun onWindowFocusChanged(hasFocus: Boolean) {
+ windowHasFocus = hasFocus
+ if (windowHasFocus && pendingCreateChannel) {
+ pendingCreateChannel = false
+ createChannel()
+ }
+ }
+
+ private fun createChannel() {
+ // Wait until window has focus so the permission prompt can be displayed
+ if (!windowHasFocus) {
+ pendingCreateChannel = true
+ return
+ }
+
+ if (notificationManager.getNotificationChannel(CHANNEL_ID_31) == null) {
+ notificationManager.createNotificationChannel(
+ NotificationChannel(
+ CHANNEL_ID_31,
+ "Foreground Services",
+ NotificationManager.IMPORTANCE_HIGH
+ )
+ )
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ if (!isFirstResume && launchActivityOnSecondResume) {
+ launchSecondActivity()
+ }
+ isFirstResume = false
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ val grantedPerms = arrayListOf<String>()
+ for ((i, permName) in permissions.withIndex()) {
+ if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
+ grantedPerms.add(permName)
+ }
+ }
+ sendBroadcast(
+ Intent(BROADCAST_ACTION)
+ .putStringArrayListExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS,
+ grantedPerms
+ )
+ .setPackage(TEST_PKG)
+ )
+ }
+
+ companion object {
+ private val TAG = CreateNotificationChannelsActivity::class.simpleName
+ }
+}
diff --git a/tests/cts/permissionui/DifferentPkgNameApp/Android.bp b/tests/cts/permissionui/DifferentPkgNameApp/Android.bp
new file mode 100644
index 000000000..3db3c30b2
--- /dev/null
+++ b/tests/cts/permissionui/DifferentPkgNameApp/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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_helper_app {
+ name: "CtsDifferentPkgNameApp",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "31",
+
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
diff --git a/tests/cts/permissionui/DifferentPkgNameApp/AndroidManifest.xml b/tests/cts/permissionui/DifferentPkgNameApp/AndroidManifest.xml
new file mode 100644
index 000000000..d0b63b597
--- /dev/null
+++ b/tests/cts/permissionui/DifferentPkgNameApp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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.permissionui.cts.usepermissionother"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="31" android:targetSdkVersion="31" />
+
+ <application android:label="EmptyActivity">
+ <activity android:name=".EmptyActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="emptyactivity.main" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/DifferentPkgNameApp/src/android/permissionui/cts/usepermissionother/EmptyActivity.kt b/tests/cts/permissionui/DifferentPkgNameApp/src/android/permissionui/cts/usepermissionother/EmptyActivity.kt
new file mode 100644
index 000000000..fc4518a90
--- /dev/null
+++ b/tests/cts/permissionui/DifferentPkgNameApp/src/android/permissionui/cts/usepermissionother/EmptyActivity.kt
@@ -0,0 +1,21 @@
+/*
+ * 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 android.permissionui.cts.usepermissionother
+
+import android.app.Activity
+
+class EmptyActivity : Activity()
diff --git a/tests/cts/permissionui/HelperAppOverlay/Android.bp b/tests/cts/permissionui/HelperAppOverlay/Android.bp
new file mode 100644
index 000000000..94d9653e9
--- /dev/null
+++ b/tests/cts/permissionui/HelperAppOverlay/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsHelperAppOverlay",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permissionui/HelperAppOverlay/AndroidManifest.xml b/tests/cts/permissionui/HelperAppOverlay/AndroidManifest.xml
new file mode 100644
index 000000000..b26459660
--- /dev/null
+++ b/tests/cts/permissionui/HelperAppOverlay/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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="android.permissionui.cts.helper.overlay">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application>
+ <activity android:name=".OverlayActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/HelperAppOverlay/src/android/permissionui/cts/helper/overlay/OverlayActivity.kt b/tests/cts/permissionui/HelperAppOverlay/src/android/permissionui/cts/helper/overlay/OverlayActivity.kt
new file mode 100644
index 000000000..0ac0fd027
--- /dev/null
+++ b/tests/cts/permissionui/HelperAppOverlay/src/android/permissionui/cts/helper/overlay/OverlayActivity.kt
@@ -0,0 +1,26 @@
+package android.permissionui.cts.helper.overlay
+
+import android.app.Activity
+import android.os.Bundle
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.WindowManager
+import android.widget.LinearLayout
+import android.widget.TextView
+
+class OverlayActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val mainLayout = LinearLayout(this)
+ mainLayout.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
+ val textView = TextView(this)
+
+ textView.text = "Find me!"
+ mainLayout.addView(textView)
+
+ val windowParams = WindowManager.LayoutParams()
+ windowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ windowManager.addView(mainLayout, windowParams)
+ }
+}
diff --git a/tests/cts/permissionui/ImplicitUserSelectStorageApp/Android.bp b/tests/cts/permissionui/ImplicitUserSelectStorageApp/Android.bp
new file mode 100644
index 000000000..c341125fd
--- /dev/null
+++ b/tests/cts/permissionui/ImplicitUserSelectStorageApp/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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_helper_app {
+ name: "CtsUsePermissionAppImplicitUserSelectStorage",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ target_sdk_version: "34",
+ min_sdk_version: "34",
+}
diff --git a/tests/cts/permissionui/ImplicitUserSelectStorageApp/AndroidManifest.xml b/tests/cts/permissionui/ImplicitUserSelectStorageApp/AndroidManifest.xml
new file mode 100644
index 000000000..212bf1508
--- /dev/null
+++ b/tests/cts/permissionui/ImplicitUserSelectStorageApp/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.permissionui.cts.usepermission">
+
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+
+ <application>
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/MediaPermissionApp33WithStorage/Android.bp b/tests/cts/permissionui/MediaPermissionApp33WithStorage/Android.bp
new file mode 100644
index 000000000..77664c40b
--- /dev/null
+++ b/tests/cts/permissionui/MediaPermissionApp33WithStorage/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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_helper_app {
+ name: "CtsMediaPermissionApp33WithStorage",
+ min_sdk_version: "33",
+ target_sdk_version: "33",
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+}
diff --git a/tests/cts/permissionui/MediaPermissionApp33WithStorage/AndroidManifest.xml b/tests/cts/permissionui/MediaPermissionApp33WithStorage/AndroidManifest.xml
new file mode 100644
index 000000000..ae21d1612
--- /dev/null
+++ b/tests/cts/permissionui/MediaPermissionApp33WithStorage/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.permissionui.cts.usepermission"
+ android:versionCode="1">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application>
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/OWNERS b/tests/cts/permissionui/OWNERS
new file mode 100644
index 000000000..01fbb4851
--- /dev/null
+++ b/tests/cts/permissionui/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
+
diff --git a/tests/cts/permissionui/PermissionPolicyApp25/Android.bp b/tests/cts/permissionui/PermissionPolicyApp25/Android.bp
new file mode 100644
index 000000000..d3f88954c
--- /dev/null
+++ b/tests/cts/permissionui/PermissionPolicyApp25/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2017 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_helper_app {
+ name: "CtsPermissionPolicyApp25",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "25",
+}
diff --git a/tests/cts/permissionui/PermissionPolicyApp25/AndroidManifest.xml b/tests/cts/permissionui/PermissionPolicyApp25/AndroidManifest.xml
new file mode 100644
index 000000000..531ea47ac
--- /dev/null
+++ b/tests/cts/permissionui/PermissionPolicyApp25/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 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.permissionui.cts.permissionpolicy">
+
+ <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25" />
+
+ <application>
+ <activity android:name=".TestProtectionFlagsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/PermissionPolicyApp25/src/android/permissionui/cts/permissionpolicy/TestProtectionFlagsActivity.kt b/tests/cts/permissionui/PermissionPolicyApp25/src/android/permissionui/cts/permissionpolicy/TestProtectionFlagsActivity.kt
new file mode 100644
index 000000000..4bc81e7a9
--- /dev/null
+++ b/tests/cts/permissionui/PermissionPolicyApp25/src/android/permissionui/cts/permissionpolicy/TestProtectionFlagsActivity.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts.permissionpolicy
+
+import android.app.Activity
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PermissionInfo
+import android.os.Bundle
+
+/** An activity that can test platform permission protection flags. */
+class TestProtectionFlagsActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setResult(
+ RESULT_OK,
+ Intent().apply {
+ putExtra("$packageName.ERROR_MESSAGE", getProtectionFlagsErrorMessage())
+ }
+ )
+ finish()
+ }
+
+ private fun getProtectionFlagsErrorMessage(): String {
+ val packageInfo = packageManager.getPackageInfo("android", PackageManager.GET_PERMISSIONS)
+ val errorMessageBuilder = StringBuilder()
+ for (declaredPermissionInfo in packageInfo.permissions ?: emptyArray()) {
+ val permissionInfo = packageManager.getPermissionInfo(declaredPermissionInfo.name, 0)
+ val protection =
+ permissionInfo.protection and
+ (PermissionInfo.PROTECTION_NORMAL or
+ PermissionInfo.PROTECTION_DANGEROUS or
+ PermissionInfo.PROTECTION_SIGNATURE or
+ PermissionInfo.PROTECTION_INTERNAL)
+ val protectionFlags = permissionInfo.protectionLevel and protection.inv()
+ if (
+ (protection == PermissionInfo.PROTECTION_NORMAL ||
+ protection == PermissionInfo.PROTECTION_DANGEROUS) && protectionFlags != 0
+ ) {
+ errorMessageBuilder.apply {
+ if (isNotEmpty()) {
+ append("\n")
+ }
+ append(
+ "Cannot add protection flags ${protectionFlagsToString(protectionFlags)
+ } to a ${protectionToString(protection)} protection permission: ${
+ permissionInfo.name}"
+ )
+ }
+ }
+ }
+ return errorMessageBuilder.toString()
+ }
+
+ private fun protectionToString(protection: Int): String =
+ when (protection) {
+ PermissionInfo.PROTECTION_NORMAL -> "normal"
+ PermissionInfo.PROTECTION_DANGEROUS -> "dangerous"
+ PermissionInfo.PROTECTION_SIGNATURE -> "signature"
+ PermissionInfo.PROTECTION_INTERNAL -> "internal"
+ else -> Integer.toHexString(protection)
+ }
+
+ private fun protectionFlagsToString(protectionFlags: Int): String {
+ var unknownProtectionFlags = protectionFlags
+ val stringBuilder = StringBuilder()
+ val appendProtectionFlag = { protectionFlag: Int, protectionFlagString: String ->
+ if (unknownProtectionFlags and protectionFlag == protectionFlag) {
+ stringBuilder.apply {
+ if (isNotEmpty()) {
+ append("|")
+ }
+ append(protectionFlagString)
+ }
+ unknownProtectionFlags = unknownProtectionFlags and protectionFlag.inv()
+ }
+ }
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_PRIVILEGED, "privileged")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_DEVELOPMENT, "development")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_APPOP, "appop")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_PRE23, "pre23")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_INSTALLER, "installer")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_VERIFIER, "verifier")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_PREINSTALLED, "preinstalled")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_SETUP, "setup")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_INSTANT, "instant")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY, "runtimeOnly")
+ appendProtectionFlag(PermissionInfo.PROTECTION_FLAG_ROLE, "role")
+ if (unknownProtectionFlags != 0) {
+ appendProtectionFlag(
+ unknownProtectionFlags,
+ Integer.toHexString(unknownProtectionFlags)
+ )
+ }
+ return stringBuilder.toString()
+ }
+}
diff --git a/tests/cts/permissionui/StorageApp33/Android.bp b/tests/cts/permissionui/StorageApp33/Android.bp
new file mode 100644
index 000000000..35c79fada
--- /dev/null
+++ b/tests/cts/permissionui/StorageApp33/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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_helper_app {
+ name: "CtsUsePermissionAppStorage33",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ target_sdk_version: "33",
+ min_sdk_version: "33",
+}
diff --git a/tests/cts/permissionui/StorageApp33/AndroidManifest.xml b/tests/cts/permissionui/StorageApp33/AndroidManifest.xml
new file mode 100644
index 000000000..212bf1508
--- /dev/null
+++ b/tests/cts/permissionui/StorageApp33/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.permissionui.cts.usepermission">
+
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+
+ <application>
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/TEST_MAPPING b/tests/cts/permissionui/TEST_MAPPING
new file mode 100644
index 000000000..444b1d5d9
--- /dev/null
+++ b/tests/cts/permissionui/TEST_MAPPING
@@ -0,0 +1,42 @@
+{
+ "presubmit-large": [
+ {
+ "name": "CtsPermissionUiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "mainline-presubmit": [
+ {
+ "name": "CtsPermissionUiTestCases[com.google.android.permission.apex]",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsPermissionUiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsPermissionUiTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsPermissionUiTestCases[com.google.android.permission.apex]"
+ }
+ ]
+}
diff --git a/tests/cts/permissionui/UsePermissionApp22/Android.bp b/tests/cts/permissionui/UsePermissionApp22/Android.bp
new file mode 100644
index 000000000..dcb04f6a4
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2015 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_helper_app {
+ name: "CtsUsePermissionApp22",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "22",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp22/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp22/AndroidManifest.xml
new file mode 100644
index 000000000..6a8bb1fd8
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22/AndroidManifest.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2015 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" />
+
+ <!-- Make sure permission code can handle invalid permissions -->
+ <uses-permission android:name="android.permissionui.cts.usepermission.INVALID_PERMISSION_NAME" />
+
+ <!-- Request two different permissions within the same group -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+
+ <!-- Contacts -->
+ <!-- Deliberately request WRITE_CONTACTS but *not* READ_CONTACTS -->
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+
+ <!-- Calendar -->
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+ <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+
+ <!-- SMS -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+ <uses-permission android:name="android.permission.RECEIVE_MMS" />
+ <uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
+
+ <!-- Storage -->
+ <!-- Special case: WRITE_EXTERNAL_STORAGE implies READ_EXTERNAL_STORAGE -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <!-- Location -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <!-- Phone -->
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.CALL_PHONE" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
+ <uses-permission android:name="android.permission.USE_SIP" />
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
+
+ <!-- Phone -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <!-- Camera -->
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <!-- Body Sensors -->
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp22CalendarOnly/Android.bp b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/Android.bp
new file mode 100644
index 000000000..26ff13566
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2015 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_helper_app {
+ name: "CtsUsePermissionApp22CalendarOnly",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "22",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp22CalendarOnly/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/AndroidManifest.xml
new file mode 100644
index 000000000..0610562b0
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" />
+
+ <!-- Calendar -->
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+ <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ <activity android:name=".StartCheckPermissionServiceActivity" android:exported="true" />
+ <service android:name=".CheckPermissionService" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/CheckPermissionService.kt b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/CheckPermissionService.kt
new file mode 100644
index 000000000..563f5255d
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/CheckPermissionService.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.IntentService
+import android.content.Intent
+import android.os.ResultReceiver
+
+/** A service that can check if a permission is currently granted */
+class CheckPermissionService : IntentService(CheckPermissionService::class.java.simpleName) {
+ companion object {
+ private const val TEST_PACKAGE_NAME = "android.permissionui.cts"
+ }
+
+ override fun onHandleIntent(intent: Intent?) {
+ val extras = intent!!.extras!!
+ // Load bundle with context of client package so ResultReceiver class can be resolved
+ val testContext =
+ createPackageContext(TEST_PACKAGE_NAME, CONTEXT_INCLUDE_CODE or CONTEXT_IGNORE_SECURITY)
+ extras.classLoader = testContext.classLoader
+ val result = extras.getParcelable<ResultReceiver>("$packageName.RESULT")!!
+ val permission = extras.getString("$packageName.PERMISSION")!!
+ result.send(checkSelfPermission(permission), null)
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/StartCheckPermissionServiceActivity.kt b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/StartCheckPermissionServiceActivity.kt
new file mode 100644
index 000000000..ebb4c2f12
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22CalendarOnly/src/android/permissionui/cts/usepermission/StartCheckPermissionServiceActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+
+class StartCheckPermissionServiceActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ startService(Intent(this, CheckPermissionService::class.java).putExtras(intent))
+ finish()
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionApp22None/Android.bp b/tests/cts/permissionui/UsePermissionApp22None/Android.bp
new file mode 100644
index 000000000..7d43f46ef
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22None/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionApp22None",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "22",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp22None/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp22None/AndroidManifest.xml
new file mode 100644
index 000000000..8ee3094ea
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp22None/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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="android.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="22" android:targetSdkVersion="22" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp23/Android.bp b/tests/cts/permissionui/UsePermissionApp23/Android.bp
new file mode 100644
index 000000000..b98b2d3a3
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp23/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2015 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_helper_app {
+ name: "CtsUsePermissionApp23",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "23",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp23/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp23/AndroidManifest.xml
new file mode 100644
index 000000000..c1bb806a5
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp23/AndroidManifest.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2015 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
+
+ <!-- Request two different permissions within the same group -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+
+ <!-- Contacts -->
+ <!-- Deliberately request WRITE_CONTACTS but *not* READ_CONTACTS -->
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+
+ <!-- Calendar -->
+ <uses-permission android:name="android.permission.READ_CALENDAR" />
+ <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+
+ <!-- SMS -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH" />
+ <uses-permission android:name="android.permission.RECEIVE_MMS" />
+ <uses-permission android:name="android.permission.READ_CELL_BROADCASTS" />
+
+ <!-- Storage -->
+ <!-- Special case: WRITE_EXTERNAL_STORAGE implies READ_EXTERNAL_STORAGE -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <!-- Location -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <!-- Phone -->
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.CALL_PHONE" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
+ <uses-permission android:name="android.permission.USE_SIP" />
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
+
+ <!-- Phone -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <!-- Camera -->
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <!-- Body Sensors -->
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp25/Android.bp b/tests/cts/permissionui/UsePermissionApp25/Android.bp
new file mode 100644
index 000000000..d16d2c486
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp25/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2017 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_helper_app {
+ name: "CtsUsePermissionApp25",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "25",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp25/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp25/AndroidManifest.xml
new file mode 100644
index 000000000..f97a6b329
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp25/AndroidManifest.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25" />
+
+ <!-- Request two different permissions within the same group -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+
+ <!-- Contacts -->
+ <!-- Deliberately request WRITE_CONTACTS but *not* READ_CONTACTS -->
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+ <!-- Calendar -->
+ <uses-permission android:name="android.permission.READ_CALENDAR"/>
+ <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
+
+ <!-- SMS -->
+ <uses-permission android:name="android.permission.SEND_SMS"/>
+ <uses-permission android:name="android.permission.RECEIVE_SMS"/>
+ <uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
+ <uses-permission android:name="android.permission.RECEIVE_MMS"/>
+ <uses-permission android:name="android.permission.READ_CELL_BROADCASTS"/>
+
+ <!-- Storage -->
+ <!-- Special case: WRITE_EXTERNAL_STORAGE implies READ_EXTERNAL_STORAGE -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
+ <!-- Location -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+
+ <!-- Phone -->
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+ <uses-permission android:name="android.permission.READ_CALL_LOG"/>
+ <uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
+ <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"/>
+ <uses-permission android:name="android.permission.USE_SIP"/>
+ <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
+
+ <!-- Phone -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+ <!-- Camera -->
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <!-- Body Sensors -->
+ <uses-permission android:name="android.permission.BODY_SENSORS"/>
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp26/Android.bp b/tests/cts/permissionui/UsePermissionApp26/Android.bp
new file mode 100644
index 000000000..d76163b3a
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp26/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2017 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_helper_app {
+ name: "CtsUsePermissionApp26",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "26",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp26/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp26/AndroidManifest.xml
new file mode 100644
index 000000000..4c29436cf
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp26/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="26" />
+
+ <!-- Request two different permissions within the same group -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp28/Android.bp b/tests/cts/permissionui/UsePermissionApp28/Android.bp
new file mode 100644
index 000000000..1f388fdc7
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp28/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsUsePermissionApp28",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "28",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp28/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp28/AndroidManifest.xml
new file mode 100644
index 000000000..98ede951f
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp28/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp29/Android.bp b/tests/cts/permissionui/UsePermissionApp29/Android.bp
new file mode 100644
index 000000000..8375b1e77
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp29/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsUsePermissionApp29",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ min_sdk_version: "29",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp29/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp29/AndroidManifest.xml
new file mode 100644
index 000000000..f1ae6c9d8
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp29/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp30/Android.bp b/tests/cts/permissionui/UsePermissionApp30/Android.bp
new file mode 100644
index 000000000..c1b4b8a9f
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionApp30",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+
+ min_sdk_version: "30",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp30/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp30/AndroidManifest.xml
new file mode 100644
index 000000000..7266fc1b6
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?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="android.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+
+ <!-- Request two different permissions within the same group -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBackground/Android.bp b/tests/cts/permissionui/UsePermissionApp30WithBackground/Android.bp
new file mode 100644
index 000000000..eeac4ff8b
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30WithBackground/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionApp30WithBackground",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+ certificate: ":cts-testkey2",
+
+ min_sdk_version: "30",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBackground/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp30WithBackground/AndroidManifest.xml
new file mode 100644
index 000000000..5949d08f2
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30WithBackground/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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="android.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application>
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt b/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
new file mode 100644
index 000000000..31ff0fa04
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30WithBackground/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+
+class RequestPermissionsActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+ return
+ }
+
+ val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
+ requestPermissions(permissions, 1)
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+ setResult(
+ RESULT_OK,
+ Intent().apply {
+ putExtra("$packageName.PERMISSIONS", permissions)
+ putExtra("$packageName.GRANT_RESULTS", grantResults)
+ }
+ )
+ finish()
+ }
+
+ companion object {
+ private val TAG = RequestPermissionsActivity::class.simpleName
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBluetooth/Android.bp b/tests/cts/permissionui/UsePermissionApp30WithBluetooth/Android.bp
new file mode 100644
index 000000000..d92ce5732
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30WithBluetooth/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsUsePermissionApp30WithBluetooth",
+ srcs: [
+ "src/**/*.kt"
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ ],
+ certificate: ":cts-testkey2",
+
+ min_sdk_version: "30",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBluetooth/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp30WithBluetooth/AndroidManifest.xml
new file mode 100644
index 000000000..4d01e99c5
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30WithBluetooth/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 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.permissionui.cts.usepermission">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+
+ <application>
+ <provider
+ android:name=".AccessBluetoothOnCommand"
+ android:authorities="android.permissionui.cts.usepermission.AccessBluetoothOnCommand"
+ android:exported="true" />
+ </application>
+
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp30WithBluetooth/src/android/permissionui/cts/usepermission/AccessBluetoothOnCommand.kt b/tests/cts/permissionui/UsePermissionApp30WithBluetooth/src/android/permissionui/cts/usepermission/AccessBluetoothOnCommand.kt
new file mode 100644
index 000000000..b19f62362
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp30WithBluetooth/src/android/permissionui/cts/usepermission/AccessBluetoothOnCommand.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permissionui.cts.usepermission
+
+import android.bluetooth.BluetoothManager
+import android.bluetooth.le.BluetoothLeScanner
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanResult
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Intent
+import android.database.Cursor
+import android.net.Uri
+import android.os.Bundle
+import android.os.SystemClock
+import android.util.Base64
+import android.util.Log
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+
+private const val LOG_TAG = "AccessBluetoothOnCommand"
+
+class AccessBluetoothOnCommand : ContentProvider() {
+
+ private enum class Result {
+ UNKNOWN,
+ ERROR,
+ EXCEPTION,
+ EMPTY,
+ FILTERED,
+ FULL
+ }
+
+ override fun call(authority: String, method: String, arg: String?, extras: Bundle?): Bundle? {
+ Log.v(LOG_TAG, "call() - start")
+ val res = Bundle()
+
+ var scanner: BluetoothLeScanner? = null
+ var scanCallback: ScanCallback? = null
+
+ try {
+
+ scanner =
+ context!!
+ .getSystemService(BluetoothManager::class.java)
+ ?.adapter!!
+ .bluetoothLeScanner
+
+ val observedScans: MutableSet<String> = ConcurrentHashMap.newKeySet()
+ val observedErrorCode = AtomicInteger(0)
+
+ scanCallback =
+ object : ScanCallback() {
+ override fun onScanResult(callbackType: Int, result: ScanResult) {
+ Log.v(LOG_TAG, "onScanResult() - result = $result")
+ observedScans.add(Base64.encodeToString(result.scanRecord!!.bytes, 0))
+ }
+
+ override fun onBatchScanResults(results: List<ScanResult>) {
+ Log.v(LOG_TAG, "onBatchScanResults() - results.size = ${results.size}")
+ for (result in results) {
+ onScanResult(0, result)
+ }
+ }
+
+ override fun onScanFailed(errorCode: Int) {
+ Log.e(LOG_TAG, "onScanFailed() - errorCode = $errorCode")
+ observedErrorCode.set(errorCode)
+ }
+ }
+
+ Log.v(LOG_TAG, "call() - startScan...")
+ scanner.startScan(scanCallback)
+
+ // Wait a few seconds to figure out what we actually observed
+ Log.v(LOG_TAG, "call() - sleep...")
+ SystemClock.sleep(3000)
+
+ if (observedErrorCode.get() > 0) {
+ Log.v(LOG_TAG, "call() observed error: ${observedErrorCode.get()}")
+ res.putInt(Intent.EXTRA_INDEX, Result.ERROR.ordinal)
+ return res
+ }
+ Log.v(LOG_TAG, "call() - (scanCount=${observedScans.size})")
+
+ when (observedScans.size) {
+ 0 -> res.putInt(Intent.EXTRA_INDEX, Result.EMPTY.ordinal)
+ 1 -> res.putInt(Intent.EXTRA_INDEX, Result.FILTERED.ordinal)
+ 5 -> res.putInt(Intent.EXTRA_INDEX, Result.FULL.ordinal)
+ else -> res.putInt(Intent.EXTRA_INDEX, Result.UNKNOWN.ordinal)
+ }
+ } catch (t: Throwable) {
+ Log.e(LOG_TAG, "call() - EXCEPTION", t)
+ res.putInt(Intent.EXTRA_INDEX, Result.EXCEPTION.ordinal)
+ } finally {
+ try {
+ Log.v(LOG_TAG, "call() - finally - stopScan...")
+ scanner!!.stopScan(scanCallback)
+ } catch (e: Exception) {
+ Log.e(LOG_TAG, "call() - finally - EXCEPTION", e)
+ }
+ }
+ Log.v(LOG_TAG, "call() - end")
+ return res
+ }
+
+ override fun onCreate(): Boolean {
+ return true
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array<String>?,
+ selection: String?,
+ selectionArgs: Array<String>?,
+ sortOrder: String?
+ ): Cursor? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getType(uri: Uri): String? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ throw UnsupportedOperationException()
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
+ throw UnsupportedOperationException()
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array<String>?
+ ): Int {
+ throw UnsupportedOperationException()
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionApp31/Android.bp b/tests/cts/permissionui/UsePermissionApp31/Android.bp
new file mode 100644
index 000000000..4424d95fe
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp31/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsUsePermissionApp31",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ target_sdk_version: "31",
+ min_sdk_version: "31",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp31/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp31/AndroidManifest.xml
new file mode 100644
index 000000000..31d025ea0
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp31/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.permissionui.cts.usepermission">
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp b/tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp
new file mode 100644
index 000000000..1d18a1a1f
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp31WithAsl/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionApp31WithAsl",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ manifest: "AndroidManifest.xml",
+ assets: ["app.metadata"],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+ target_sdk_version: "31",
+ min_sdk_version: "31",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml
new file mode 100644
index 000000000..02126f1b4
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp31WithAsl/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permissionui.cts.usepermission">
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ <property android:name="android.content.SAFETY_LABEL_PATH"
+ android:value="assets/app.metadata"/>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata b/tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata
new file mode 100644
index 000000000..d83d081d8
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp31WithAsl/app.metadata
@@ -0,0 +1,17 @@
+<bundle>
+<long name="version" value="1" />
+<pbundle_as_map name="safety_labels">
+<pbundle_as_map name="data_labels">
+<pbundle_as_map name="data_shared">
+<pbundle_as_map name="location">
+<pbundle_as_map name="approx_location">
+<int-array name="purposes" num="1">
+<item value="5" />
+</int-array>
+</pbundle_as_map>
+</pbundle_as_map>
+</pbundle_as_map>
+</pbundle_as_map>
+<long name="version" value="1" />
+</pbundle_as_map>
+</bundle>
diff --git a/tests/cts/permissionui/UsePermissionApp32/Android.bp b/tests/cts/permissionui/UsePermissionApp32/Android.bp
new file mode 100644
index 000000000..874af0e06
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp32/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsUsePermissionApp32",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+
+ min_sdk_version: "32",
+ target_sdk_version: "32",
+}
diff --git a/tests/cts/permissionui/UsePermissionApp32/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionApp32/AndroidManifest.xml
new file mode 100644
index 000000000..923139e66
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionApp32/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.permissionui.cts.usepermission">
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application>
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionAppLatest/Android.bp b/tests/cts/permissionui/UsePermissionAppLatest/Android.bp
new file mode 100644
index 000000000..c9e9f8fb0
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatest/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2017 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"],
+}
+
+filegroup {
+ name: "CtsUsePermissionAppSrc",
+ srcs: [
+ "src/**/*.kt",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionAppLatest",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "30",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml
new file mode 100644
index 000000000..0b92f5ef1
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatest/AndroidManifest.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 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.permissionui.cts.usepermission">
+
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.CALL_PHONE"/>
+
+ <!-- Request two different permissions within the same group -->
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
+
+ <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
+ <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+ <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+ <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
+ <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ <activity-alias
+ android:name=".ViewPermissionUsageActivity"
+ android:exported="true"
+ android:permission="android.permission.START_VIEW_PERMISSION_USAGE"
+ android:targetActivity=".FinishOnCreateActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
+ </intent-filter>
+ </activity-alias>
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/CheckCalendarAccessActivity.kt b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/CheckCalendarAccessActivity.kt
new file mode 100644
index 000000000..55fd104cb
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/CheckCalendarAccessActivity.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.content.ContentValues
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.provider.CalendarContract
+
+/** An activity that can check calendar access. */
+class CheckCalendarAccessActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val supportsRuntimePermissions = applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M
+ val hasAccess: Boolean
+ val uri =
+ try {
+ contentResolver.insert(
+ CalendarContract.Calendars.CONTENT_URI,
+ ContentValues().apply {
+ put(CalendarContract.Calendars.NAME, "cts" + System.nanoTime())
+ put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, "cts")
+ put(CalendarContract.Calendars.CALENDAR_COLOR, 0xffff0000)
+ }
+ )!!
+ } catch (e: SecurityException) {
+ null
+ }
+ hasAccess =
+ if (uri != null) {
+ val count = contentResolver.query(uri, null, null, null).use { it!!.count }
+ if (supportsRuntimePermissions) {
+ assert(count == 1)
+ true
+ } else {
+ // Without access we're handed back a "fake" Uri that doesn't contain
+ // any of the data we tried persisting
+ assert(count == 0 || count == 1)
+ count == 1
+ }
+ } else {
+ assert(supportsRuntimePermissions)
+ try {
+ contentResolver
+ .query(CalendarContract.Calendars.CONTENT_URI, null, null, null)
+ .use {}
+ error("Expected SecurityException")
+ } catch (e: SecurityException) {}
+ false
+ }
+ setResult(RESULT_OK, Intent().apply { putExtra("$packageName.HAS_ACCESS", hasAccess) })
+ finish()
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/FinishOnCreateActivity.kt b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/FinishOnCreateActivity.kt
new file mode 100644
index 000000000..693a8c8f9
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/FinishOnCreateActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.os.Bundle
+
+class FinishOnCreateActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setResult(RESULT_OK)
+ finish()
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
new file mode 100644
index 000000000..3eea77340
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatest/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import com.android.modules.utils.build.SdkLevel
+
+class RequestPermissionsActivity : Activity() {
+
+ private var shouldAskTwice = false
+ private var timesAsked = 0
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+ return
+ }
+
+ val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
+ shouldAskTwice = intent.getBooleanExtra("$packageName.ASK_TWICE", false)
+ if (SdkLevel.isAtLeastV()) {
+ // TODO: make deviceId dynamic
+ requestPermissions(permissions, 1, Context.DEVICE_ID_DEFAULT)
+ } else {
+ requestPermissions(permissions, 1)
+ }
+ timesAsked = 1
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray
+ ) {
+ handleResult(permissions, grantResults)
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray,
+ deviceId: Int
+ ) {
+ handleResult(permissions, grantResults, deviceId)
+ }
+
+ private fun handleResult(
+ permissions: Array<out String>,
+ grantResults: IntArray,
+ deviceId: Int? = null
+ ) {
+ if (shouldAskTwice && timesAsked < 2) {
+ requestPermissions(permissions, 1)
+ timesAsked += 1
+ return
+ }
+
+ setResult(
+ RESULT_OK,
+ Intent().apply {
+ putExtra("$packageName.PERMISSIONS", permissions)
+ putExtra("$packageName.GRANT_RESULTS", grantResults)
+ if (deviceId != null) {
+ putExtra("$packageName.DEVICE_ID", deviceId)
+ }
+ }
+ )
+ finish()
+ }
+
+ companion object {
+ private val TAG = RequestPermissionsActivity::class.simpleName
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionAppLatestNone/Android.bp b/tests/cts/permissionui/UsePermissionAppLatestNone/Android.bp
new file mode 100644
index 000000000..cbc934eef
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatestNone/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionAppLatestNone",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "30",
+ srcs: [
+ ":CtsUsePermissionAppSrc",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ "compatibility-device-util-axt",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permissionui/UsePermissionAppLatestNone/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionAppLatestNone/AndroidManifest.xml
new file mode 100644
index 000000000..49103769e
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLatestNone/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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="android.permissionui.cts.usepermission">
+
+ <application>
+ <activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
+ <activity android:name=".FinishOnCreateActivity" android:exported="true" />
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionAppLocationProvider/Android.bp b/tests/cts/permissionui/UsePermissionAppLocationProvider/Android.bp
new file mode 100644
index 000000000..a2cd88d4e
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLocationProvider/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsAccessMicrophoneAppLocationProvider",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "31",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+} \ No newline at end of file
diff --git a/tests/cts/permissionui/UsePermissionAppLocationProvider/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionAppLocationProvider/AndroidManifest.xml
new file mode 100644
index 000000000..03edc78a2
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLocationProvider/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 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.permissionui.cts.accessmicrophoneapplocationprovider">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+ <attribution
+ android:label="@string/attribution_label"
+ android:tag="test.tag" />
+
+ <application
+ android:attributionsAreUserVisible="true"
+ android:label="LocationProviderWithMicApp">
+ <activity android:name=".AddLocationProviderActivity" android:exported="true" />
+ <activity android:name=".UseMicrophoneActivity" android:exported="true"/>
+ </application>
+</manifest>
diff --git a/PermissionController/res/values-w764dp-v33/dimens.xml b/tests/cts/permissionui/UsePermissionAppLocationProvider/res/values/strings.xml
index 78b4675f2..9682d7f8a 100644
--- a/PermissionController/res/values-w764dp-v33/dimens.xml
+++ b/tests/cts/permissionui/UsePermissionAppLocationProvider/res/values/strings.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
<!--
- ~ Copyright (C) 2022 The Android Open Source Project
+ ~ Copyright (C) 2021 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.
@@ -15,5 +17,5 @@
-->
<resources>
- <dimen name="sc_button_horizontal_padding">@dimen/sc_large_screen_button_padding</dimen>
+ <string name="attribution_label">Attribution Label</string>
</resources>
diff --git a/tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/AddLocationProviderActivity.kt b/tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/AddLocationProviderActivity.kt
new file mode 100644
index 000000000..b4bd16f05
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/AddLocationProviderActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permissionui.cts.accessmicrophoneapplocationprovider
+
+import android.app.Activity
+import android.location.Criteria
+import android.location.LocationManager
+import android.os.Bundle
+
+/** An activity that adds this package as a test location provider and uses microphone. */
+class AddLocationProviderActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val attrContext = createAttributionContext("test.tag")
+ val locationManager = attrContext.getSystemService(LocationManager::class.java)!!
+ locationManager.addTestProvider(
+ packageName,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ Criteria.POWER_LOW,
+ Criteria.ACCURACY_COARSE
+ )
+
+ setResult(RESULT_OK)
+ finish()
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/UseMicrophoneActivity.kt b/tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/UseMicrophoneActivity.kt
new file mode 100644
index 000000000..897949b95
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppLocationProvider/src/android/permissionui/cts/accessmicrophoneapplocationprovider/UseMicrophoneActivity.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permissionui.cts.accessmicrophoneapplocationprovider
+
+import android.app.Activity
+import android.content.Context
+import android.media.AudioFormat
+import android.media.AudioRecord
+import android.media.MediaRecorder
+import android.os.Bundle
+import android.os.Handler
+
+private const val USE_DURATION_MS = 10000L
+private const val SAMPLE_RATE_HZ = 44100
+
+/** An activity that uses microphone. */
+class UseMicrophoneActivity : Activity() {
+ private var recorder: AudioRecord? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val attrContext = createAttributionContext("test.tag")
+ useMic(attrContext)
+ setResult(RESULT_OK)
+ finish()
+ }
+
+ override fun finish() {
+ recorder?.stop()
+ recorder = null
+ super.finish()
+ }
+
+ override fun onStop() {
+ super.onStop()
+ finish()
+ }
+
+ private fun useMic(context: Context) {
+ recorder =
+ AudioRecord.Builder()
+ .setAudioSource(MediaRecorder.AudioSource.MIC)
+ .setAudioFormat(
+ AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(SAMPLE_RATE_HZ)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build()
+ )
+ .setContext(context)
+ .build()
+ recorder?.startRecording()
+ Handler().postDelayed({ finish() }, USE_DURATION_MS)
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/Android.bp b/tests/cts/permissionui/UsePermissionAppWithOverlay/Android.bp
new file mode 100644
index 000000000..6ae324577
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsUsePermissionAppWithOverlay",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+ certificate: ":cts-testkey2",
+}
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/AndroidManifest.xml b/tests/cts/permissionui/UsePermissionAppWithOverlay/AndroidManifest.xml
new file mode 100644
index 000000000..037ff548f
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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="android.permissionui.cts.usepermission">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <application>
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ <activity android:name=".OverlayActivity"
+ android:theme="@style/OverlayTheme" />
+ </application>
+</manifest>
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/res/drawable/border.xml b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/drawable/border.xml
new file mode 100644
index 000000000..1e637be61
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/drawable/border.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#FFFFFF" />
+ <corners
+ android:radius="4dp"/>
+ <stroke
+ android:width="4dp"
+ android:color="@android:color/black" />
+</shape>
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml
new file mode 100644
index 000000000..ea355f761
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/layout/overlay_activity.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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/overlay"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/border"
+ android:padding="8dp" >
+
+ <View android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <TextView android:id="@+id/overlay_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/black"
+ android:text="@string/app_description" />
+</LinearLayout> \ No newline at end of file
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/strings.xml b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/strings.xml
new file mode 100644
index 000000000..ca0b4ffc1
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<resources>
+ <string name="app_description">This activity attempts to tapjack the activity below.\nAny security sensitive controls below should not respond to taps as long as this activity is visible.</string>
+</resources> \ No newline at end of file
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/styles.xml b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/styles.xml
new file mode 100644
index 000000000..880d94037
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/res/values/styles.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<resources>
+ <style name="OverlayTheme" parent="android:Theme.Dialog">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ </style>
+</resources>
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/OverlayActivity.kt b/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/OverlayActivity.kt
new file mode 100644
index 000000000..bcd5496dc
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/OverlayActivity.kt
@@ -0,0 +1,60 @@
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Bundle
+import android.view.Gravity
+import android.view.WindowManager
+
+class OverlayActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.overlay_activity)
+ val params = window.attributes
+ params.flags =
+ (WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
+
+ if (!intent.getBooleanExtra(EXTRA_FULL_OVERLAY, true)) {
+ params.gravity = Gravity.LEFT or Gravity.TOP
+ val left = intent.getIntExtra(OVERLAY_LEFT, params.x)
+ val top = intent.getIntExtra(OVERLAY_TOP, params.y)
+ val right = intent.getIntExtra(OVERLAY_RIGHT, params.x + params.width)
+ val bottom = intent.getIntExtra(OVERLAY_BOTTOM, top + 1)
+ params.x = left
+ params.y = top
+ params.width = right - left
+ params.height = bottom - top
+ }
+
+ registerReceiver(
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action != RequestPermissionsActivity.ACTION_HIDE_OVERLAY) {
+ return
+ }
+
+ finish()
+ }
+ },
+ IntentFilter(RequestPermissionsActivity.ACTION_HIDE_OVERLAY),
+ RECEIVER_EXPORTED
+ )
+ }
+
+ companion object {
+ const val EXTRA_FULL_OVERLAY = "android.permissionui.cts.usepermission.extra.FULL_OVERLAY"
+
+ const val OVERLAY_LEFT = "android.permissionui.cts.usepermission.extra.OVERLAY_LEFT"
+ const val OVERLAY_TOP = "android.permissionui.cts.usepermission.extra.OVERLAY_TOP"
+ const val OVERLAY_RIGHT = "android.permissionui.cts.usepermission.extra.OVERLAY_RIGHT"
+ const val OVERLAY_BOTTOM = "android.permissionui.cts.usepermission.extra.OVERLAY_BOTTOM"
+ }
+}
diff --git a/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt b/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
new file mode 100644
index 000000000..c13a02392
--- /dev/null
+++ b/tests/cts/permissionui/UsePermissionAppWithOverlay/src/android/permissionui/cts/usepermission/RequestPermissionsActivity.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts.usepermission
+
+import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Bundle
+import android.os.Handler
+import android.util.Log
+
+class RequestPermissionsActivity : Activity() {
+
+ var paused = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+ return
+ }
+
+ registerReceiver(
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (intent?.action != ACTION_SHOW_OVERLAY) {
+ return
+ }
+
+ startActivity(
+ intent
+ .setAction(null)
+ .setComponent(ComponentName(context!!, OverlayActivity::class.java))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ }
+ },
+ IntentFilter(ACTION_SHOW_OVERLAY),
+ RECEIVER_EXPORTED
+ )
+ Handler(mainLooper).post(this::eventuallyRequestPermission)
+ }
+
+ /**
+ * Keep trying to requestPermissions until the dialog shows. It may fail the first few times due
+ * to rapid install/uninstall tests do
+ */
+ private fun eventuallyRequestPermission() {
+ if (!paused) {
+ val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
+ requestPermissions(permissions, 1)
+ Handler(mainLooper).postDelayed(this::eventuallyRequestPermission, 200)
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+ setResult(
+ RESULT_OK,
+ Intent().apply {
+ putExtra("$packageName.PERMISSIONS", permissions)
+ putExtra("$packageName.GRANT_RESULTS", grantResults)
+ }
+ )
+ finish()
+ }
+
+ override fun onPause() {
+ paused = true
+ super.onPause()
+ }
+
+ override fun onResume() {
+ paused = false
+ super.onResume()
+ }
+
+ companion object {
+ const val ACTION_SHOW_OVERLAY = "android.permissionui.cts.usepermission.ACTION_SHOW_OVERLAY"
+ const val ACTION_HIDE_OVERLAY = "android.permissionui.cts.usepermission.ACTION_HIDE_OVERLAY"
+ private val TAG = RequestPermissionsActivity::class.simpleName
+ }
+}
diff --git a/tests/cts/permissionui/res/raw/lg_g4_iso_800_jpg.jpg b/tests/cts/permissionui/res/raw/lg_g4_iso_800_jpg.jpg
new file mode 100644
index 000000000..d26419604
--- /dev/null
+++ b/tests/cts/permissionui/res/raw/lg_g4_iso_800_jpg.jpg
Binary files differ
diff --git a/tests/cts/permissionui/res/raw/test_video.mp4 b/tests/cts/permissionui/res/raw/test_video.mp4
new file mode 100644
index 000000000..ab95ac07d
--- /dev/null
+++ b/tests/cts/permissionui/res/raw/test_video.mp4
Binary files differ
diff --git a/PermissionController/res/values-w764dp-v34/dimens.xml b/tests/cts/permissionui/res/values-en-rGB/strings.xml
index cb336fc3e..7c98df768 100644..100755
--- a/PermissionController/res/values-w764dp-v34/dimens.xml
+++ b/tests/cts/permissionui/res/values-en-rGB/strings.xml
@@ -1,5 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
<!--
- ~ Copyright (C) 2022 The Android Open Source Project
+ ~ Copyright (C) 2017 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.
@@ -15,5 +17,5 @@
-->
<resources>
- <dimen name="sc_action_button_list_margin">@dimen/sc_spacing_large</dimen>
+ <string name="permissions">Permission</string>
</resources>
diff --git a/tests/cts/permissionui/res/values/strings.xml b/tests/cts/permissionui/res/values/strings.xml
new file mode 100755
index 000000000..b2f8e3dac
--- /dev/null
+++ b/tests/cts/permissionui/res/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2017 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>
+ <string name="permissions">Permissions</string>
+ <string name="allow_foreground">Allow only while using the app</string>
+ <string name="allow_media_storage">Allow access to media only</string>
+ <string name="allow_external_storage">Allow management of all files</string>
+ <string name="car_mic_privacy_chip_id">com.android.systemui:id/mic_privacy_chip</string>
+ <string name="car_camera_privacy_chip_id">com.android.systemui:id/camera_privacy_chip</string>
+ <string name="test_accessibility_service">TestService1</string>
+ <string name="test_accessibility_service_2">TestService2</string>
+</resources>
diff --git a/tests/cts/permissionui/res/xml/test_accessibilityservice.xml b/tests/cts/permissionui/res/xml/test_accessibilityservice.xml
new file mode 100644
index 000000000..fa87e2e0f
--- /dev/null
+++ b/tests/cts/permissionui/res/xml/test_accessibilityservice.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:canRetrieveWindowContent="true"
+ android:accessibilityFlags="flagDefault"
+ android:notificationTimeout="0" />
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt
new file mode 100644
index 000000000..0db639d49
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/AppDataSharingUpdatesTest.kt
@@ -0,0 +1,727 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_OTHER
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_STORE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED
+import android.os.Build
+import android.os.PersistableBundle
+import android.permission.cts.PermissionUtils
+import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingAds
+import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingNoAds
+import android.permissionui.cts.AppMetadata.createAppMetadataWithNoSharing
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.provider.DeviceConfig
+import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED
+import android.util.Log
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.waitForBroadcasts
+import com.android.modules.utils.build.SdkLevel
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+
+/** Tests the UI that displays information about apps' updates to their data sharing policies. */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@FlakyTest
+class AppDataSharingUpdatesTest : BaseUsePermissionTest() {
+ // TODO(b/263838456): Add tests for personal and work profile.
+
+ private var activityManager: ActivityManager? = null
+
+ @get:Rule
+ val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @get:Rule
+ val deviceConfigSafetyLabelChangeNotificationsEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED,
+ true.toString()
+ )
+
+ @get:Rule
+ val deviceConfigDataSharingUpdatesPeriod =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS,
+ "600000"
+ )
+
+ /**
+ * This rule serves to limit the max number of safety labels that can be persisted, so that
+ * repeated tests don't overwhelm the disk storage on the device.
+ */
+ @get:Rule
+ val deviceConfigMaxSafetyLabelsPersistedPerApp =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP,
+ "2"
+ )
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(
+ "Data sharing updates page is only available on U+",
+ SdkLevel.isAtLeastU()
+ )
+ Assume.assumeFalse(isAutomotive)
+ Assume.assumeFalse(isTv)
+ Assume.assumeFalse(isWatch)
+
+ PermissionUtils.clearAppState(context.packageManager.permissionControllerPackageName)
+ waitForBroadcasts()
+ activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedCoarseLocation_noSharingToNoAdsSharing_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantCoarseLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedFineLocation_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedBackgroundLocation_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantBackgroundLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_noSharingToAdsSharing_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingAds())
+ grantCoarseLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES_FOR_ADS), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_noAdsSharingToAdsSharing_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingAds())
+ grantCoarseLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES_FOR_ADS), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_adsSharingToNoAdsSharing_showsNoUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingAds(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantCoarseLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), false)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_noAdsSharingToNoSharing_showsNoUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ grantCoarseLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), false)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_adsSharingToNoSharing_showsNoUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingAds(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ grantCoarseLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), false)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Ignore("b/282063206")
+ @Test
+ fun clickLearnMore_opensHelpCenter() {
+ Assume.assumeFalse(getPermissionControllerResString(HELP_CENTER_URL_ID).isNullOrEmpty())
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(LEARN_ABOUT_DATA_SHARING), true)
+
+ clickAndWaitForWindowTransition(By.textContains(LEARN_ABOUT_DATA_SHARING))
+
+ eventually({ assertHelpCenterLinkClickSuccessful() }, HELP_CENTER_TIMEOUT_MILLIS)
+ } finally {
+ pressBack()
+ pressBack()
+ }
+ }
+
+ @Test
+ fun noHelpCenterLinkAvailable_noHelpCenterClickAction() {
+ Assume.assumeTrue(getPermissionControllerResString(HELP_CENTER_URL_ID).isNullOrEmpty())
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(LEARN_ABOUT_DATA_SHARING), false)
+ } finally {
+ pressBack()
+ pressBack()
+ }
+ }
+
+ @Test
+ fun clickUpdate_opensAppLocationPermissionPage() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(UPDATES_IN_LAST_30_DAYS), true)
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+
+ clickAndWaitForWindowTransition(By.textContains(APP_PACKAGE_NAME_SUBSTRING))
+
+ findView(By.descContains(LOCATION_PERMISSION), true)
+ findView(By.textContains(APP_PACKAGE_NAME), true)
+ } finally {
+ pressBack()
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppNotGrantedLocation_showsNoUpdates() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), false)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_noMetadata_showsNoUpdates() {
+ installPackageWithoutInstallSource(APP_APK_PATH_31)
+ waitForBroadcasts()
+ installPackageWithoutInstallSource(APP_APK_PATH_31)
+ waitForBroadcasts()
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), false)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_featureDisabled_doesNotOpenDataSharingUpdatesPage() {
+ setDeviceConfigPrivacyProperty(SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false.toString())
+
+ startAppDataSharingUpdatesActivity()
+
+ findView(By.descContains(DATA_SHARING_UPDATES), false)
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceUnspecified_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_UNSPECIFIED,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_UNSPECIFIED
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceOther_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_OTHER,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_OTHER
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceStore_showsUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_STORE,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_STORE
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertUpdatesPresent()
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(NOW_SHARED_WITH_THIRD_PARTIES), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceLocalFile_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_LOCAL_FILE,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_LOCAL_FILE
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceDownloaded_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceUnspecified_asAslInApk_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_UNSPECIFIED,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_UNSPECIFIED
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceOther_asAslInApk_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_OTHER,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_OTHER
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceStore_asAslInApk_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_STORE,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_STORE
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceLocalFile_asAslInApk_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_LOCAL_FILE,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_LOCAL_FILE
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun startActivityWithIntent_whenAppGrantedLocation_packageSourceDownloaded_asAslInApk_doesntShowUpdate() {
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE,
+ waitTillBroadcastProcessed = true
+ )
+ installAndWaitTillPackageAdded(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ grantFineLocationPermission(APP_PACKAGE_NAME)
+
+ startAppDataSharingUpdatesActivity()
+
+ try {
+ assertNoUpdatesPresent()
+ } finally {
+ pressBack()
+ }
+ }
+
+ /** Installs an app and waits for the package added broadcast to be dispatched. */
+ private fun installAndWaitTillPackageAdded(
+ apkPath: String,
+ appMetadata: PersistableBundle? = null,
+ packageSource: Int? = null,
+ waitTillBroadcastProcessed: Boolean = false
+ ) {
+ installPackageViaSession(apkPath, appMetadata, packageSource)
+ waitForBroadcasts()
+ // TODO(b/279455955): Investigate why this is necessary and remove if possible.
+ if (waitTillBroadcastProcessed) Thread.sleep(500)
+ }
+
+ private fun assertUpdatesPresent() {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_SUBTITLE), true)
+ findView(By.textContains(UPDATES_IN_LAST_30_DAYS), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_FOOTER_MESSAGE), true)
+ findView(By.textContains(LEARN_ABOUT_DATA_SHARING), shouldShowLearnMoreLink())
+ }
+
+ private fun assertNoUpdatesPresent() {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_SUBTITLE), true)
+ findView(By.textContains(DATA_SHARING_NO_UPDATES_MESSAGE), true)
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), false)
+ findView(By.textContains(UPDATES_IN_LAST_30_DAYS), false)
+ findView(By.textContains(DATA_SHARING_UPDATES_FOOTER_MESSAGE), true)
+ findView(By.textContains(LEARN_ABOUT_DATA_SHARING), shouldShowLearnMoreLink())
+ }
+
+ private fun grantFineLocationPermission(packageName: String) {
+ uiAutomation.grantRuntimePermission(
+ packageName,
+ android.Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+ private fun grantCoarseLocationPermission(packageName: String) {
+ uiAutomation.grantRuntimePermission(
+ packageName,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ }
+ private fun grantBackgroundLocationPermission(packageName: String) {
+ uiAutomation.grantRuntimePermission(
+ packageName,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ )
+ }
+
+ private fun assertHelpCenterLinkClickSuccessful() {
+ runWithShellPermissionIdentity {
+ val runningTasks = activityManager!!.getRunningTasks(5)
+
+ Log.v(TAG, "# running tasks: ${runningTasks.size}")
+ assertFalse("Expected runningTasks to not be empty", runningTasks.isEmpty())
+
+ runningTasks.forEachIndexed { index, runningTaskInfo ->
+ Log.v(TAG, "task $index ${runningTaskInfo.baseIntent}")
+ }
+
+ val taskInfo = runningTasks[0]
+ val observedIntentAction = taskInfo.baseIntent.action
+ val observedIntentDataString = taskInfo.baseIntent.dataString
+ val observedIntentScheme: String? = taskInfo.baseIntent.scheme
+
+ Log.v(TAG, "task base intent: ${taskInfo.baseIntent}")
+ assertEquals("Unexpected intent action", Intent.ACTION_VIEW, observedIntentAction)
+
+ val expectedUrl = getPermissionControllerResString(HELP_CENTER_URL_ID)!!
+ assertFalse(observedIntentDataString.isNullOrEmpty())
+ assertTrue(observedIntentDataString?.startsWith(expectedUrl) ?: false)
+
+ assertFalse(observedIntentScheme.isNullOrEmpty())
+ assertEquals("https", observedIntentScheme)
+ }
+ }
+
+ private fun shouldShowLearnMoreLink(): Boolean {
+ return !getPermissionControllerResString(HELP_CENTER_URL_ID).isNullOrEmpty()
+ }
+
+ /** Companion object for [AppDataSharingUpdatesTest]. */
+ companion object {
+ private val TAG = AppDataSharingUpdatesTest::class.java.simpleName
+
+ private const val HELP_CENTER_URL_ID = "data_sharing_help_center_link"
+ private const val HELP_CENTER_TIMEOUT_MILLIS: Long = 20000
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppMetadata.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppMetadata.kt
new file mode 100644
index 000000000..8c61f0366
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/AppMetadata.kt
@@ -0,0 +1,214 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.os.PersistableBundle
+
+/** Helper methods for creating test app metadata [PersistableBundle] */
+object AppMetadata {
+ /** Returns empty App Metadata [PersistableBundle] representation */
+ fun createEmptyAppMetadata(): PersistableBundle {
+ return PersistableBundle()
+ }
+
+ /** Returns valid App Metadata [PersistableBundle] representation */
+ fun createDefaultAppMetadata(): PersistableBundle {
+ val approximateLocationBundle =
+ PersistableBundle().apply { putIntArray(KEY_PURPOSES, (1..7).toList().toIntArray()) }
+
+ val locationBundle =
+ PersistableBundle().apply {
+ putPersistableBundle(APPROX_LOCATION, approximateLocationBundle)
+ }
+
+ val dataSharedBundle =
+ PersistableBundle().apply { putPersistableBundle(LOCATION_CATEGORY, locationBundle) }
+
+ val dataLabelBundle =
+ PersistableBundle().apply { putPersistableBundle(KEY_DATA_SHARED, dataSharedBundle) }
+
+ val safetyLabelBundle =
+ PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_SAFETY_LABELS_VERSION)
+ putPersistableBundle(KEY_DATA_LABELS, dataLabelBundle)
+ }
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_SAFETY_LABELS, safetyLabelBundle)
+ }
+ }
+
+ /**
+ * Returns invalid App Metadata [PersistableBundle] representation. Invalidity due to invalid
+ * label name usage
+ */
+ fun createInvalidAppMetadata(): PersistableBundle {
+ val validAppMetaData = createDefaultAppMetadata()
+ val validSafetyLabel = validAppMetaData.getPersistableBundle(KEY_SAFETY_LABELS)
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_INVALID, validSafetyLabel)
+ }
+ }
+
+ /**
+ * Returns invalid App Metadata [PersistableBundle] representation. Invalidity due to no top
+ * level meta data version number.
+ */
+ fun createInvalidAppMetadataWithoutTopLevelVersion(): PersistableBundle {
+ val validAppMetaData = createDefaultAppMetadata()
+ val validSafetyLabel = validAppMetaData.getPersistableBundle(KEY_SAFETY_LABELS)
+
+ return PersistableBundle().apply {
+ putPersistableBundle(KEY_SAFETY_LABELS, validSafetyLabel)
+ }
+ }
+
+ /**
+ * Returns invalid App Metadata [PersistableBundle] representation. Invalidity due to invalid
+ * top level meta data version number.
+ */
+ fun createInvalidAppMetadataWithInvalidTopLevelVersion(): PersistableBundle {
+ val validAppMetaData = createDefaultAppMetadata()
+ val validSafetyLabel = validAppMetaData.getPersistableBundle(KEY_SAFETY_LABELS)
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INVALID_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_SAFETY_LABELS, validSafetyLabel)
+ }
+ }
+
+ /**
+ * Returns invalid App Metadata [PersistableBundle] representation. Invalidity due to no safety
+ * label version number.
+ */
+ fun createInvalidAppMetadataWithoutSafetyLabelVersion(): PersistableBundle {
+ val validAppMetaData = createDefaultAppMetadata()
+ val invalidSafetyLabel =
+ validAppMetaData.getPersistableBundle(KEY_SAFETY_LABELS).apply {
+ this?.remove(KEY_VERSION)
+ }
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_SAFETY_LABELS, invalidSafetyLabel)
+ }
+ }
+
+ /**
+ * Returns invalid App Metadata [PersistableBundle] representation. Invalidity due to invalid
+ * safety label version number.
+ */
+ fun createInvalidAppMetadataWithInvalidSafetyLabelVersion(): PersistableBundle {
+ val validAppMetaData = createDefaultAppMetadata()
+ val invalidSafetyLabel =
+ validAppMetaData.getPersistableBundle(KEY_SAFETY_LABELS)?.apply {
+ putLong(KEY_VERSION, INVALID_SAFETY_LABELS_VERSION)
+ }
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_SAFETY_LABELS, invalidSafetyLabel)
+ }
+ }
+ /** Returns an App Metadata [PersistableBundle] representation where no data is shared. */
+ fun createAppMetadataWithNoSharing(): PersistableBundle {
+ return createMetadataWithDataShared(PersistableBundle())
+ }
+
+ /**
+ * Returns an App Metadata [PersistableBundle] representation where location data is shared, but
+ * not for advertising purpose.
+ */
+ fun createAppMetadataWithLocationSharingNoAds(): PersistableBundle {
+ val locationBundle =
+ PersistableBundle().apply {
+ putPersistableBundle(
+ APPROX_LOCATION,
+ PersistableBundle().apply {
+ putIntArray(
+ KEY_PURPOSES,
+ listOf(PURPOSE_FRAUD_PREVENTION_SECURITY).toIntArray()
+ )
+ }
+ )
+ }
+
+ val dataSharedBundle =
+ PersistableBundle().apply { putPersistableBundle(LOCATION_CATEGORY, locationBundle) }
+
+ return createMetadataWithDataShared(dataSharedBundle)
+ }
+
+ /**
+ * Returns an App Metadata [PersistableBundle] representation where location data is shared,
+ * including for advertising purpose.
+ */
+ fun createAppMetadataWithLocationSharingAds(): PersistableBundle {
+ val locationBundle =
+ PersistableBundle().apply {
+ putPersistableBundle(
+ APPROX_LOCATION,
+ PersistableBundle().apply {
+ putIntArray(KEY_PURPOSES, listOf(PURPOSE_ADVERTISING).toIntArray())
+ }
+ )
+ }
+
+ val dataSharedBundle =
+ PersistableBundle().apply { putPersistableBundle(LOCATION_CATEGORY, locationBundle) }
+
+ return createMetadataWithDataShared(dataSharedBundle)
+ }
+
+ private fun createMetadataWithDataShared(
+ dataSharedBundle: PersistableBundle
+ ): PersistableBundle {
+ val dataLabelBundle =
+ PersistableBundle().apply { putPersistableBundle(KEY_DATA_SHARED, dataSharedBundle) }
+
+ val safetyLabelBundle =
+ PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_SAFETY_LABELS_VERSION)
+ putPersistableBundle(KEY_DATA_LABELS, dataLabelBundle)
+ }
+
+ return PersistableBundle().apply {
+ putLong(KEY_VERSION, INITIAL_TOP_LEVEL_VERSION)
+ putPersistableBundle(KEY_SAFETY_LABELS, safetyLabelBundle)
+ }
+ }
+
+ private const val INITIAL_SAFETY_LABELS_VERSION = 1L
+ private const val INITIAL_TOP_LEVEL_VERSION = 1L
+ private const val INVALID_SAFETY_LABELS_VERSION = -1L
+ private const val INVALID_TOP_LEVEL_VERSION = 0L
+
+ private const val LOCATION_CATEGORY = "location"
+ private const val APPROX_LOCATION = "approx_location"
+ private const val PURPOSE_FRAUD_PREVENTION_SECURITY = 4
+ private const val PURPOSE_ADVERTISING = 5
+
+ private const val KEY_VERSION = "version"
+ private const val KEY_SAFETY_LABELS = "safety_labels"
+ private const val KEY_INVALID = "invalid_safety_labels"
+ private const val KEY_DATA_SHARED = "data_shared"
+ private const val KEY_DATA_LABELS = "data_labels"
+ private const val KEY_PURPOSES = "purposes"
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt
new file mode 100644
index 000000000..6476bab06
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/AppPermissionTest.kt
@@ -0,0 +1,381 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission_group.PHONE
+import android.Manifest.permission_group.SMS
+import android.os.Build
+import android.permission.flags.Flags
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.provider.Settings.Secure.USER_SETUP_COMPLETE
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.modules.utils.build.SdkLevel
+import com.google.common.truth.Truth
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+/** App Permission page UI tests. */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@FlakyTest
+class AppPermissionTest : BaseUsePermissionTest() {
+
+ @get:Rule
+ val deviceConfigPermissionRationaleEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PERMISSION_RATIONALE_ENABLED,
+ true.toString()
+ )
+
+ @get:Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue("Permission rationale is only available on U+", SdkLevel.isAtLeastU())
+ Assume.assumeFalse(isAutomotive)
+ Assume.assumeFalse(isTv)
+ Assume.assumeFalse(isWatch)
+
+ val userSetupComplete =
+ Settings.Secure.getInt(context.contentResolver, USER_SETUP_COMPLETE, 0) == 1
+
+ Truth.assertWithMessage("User setup must be complete before running this test")
+ .that(userSetupComplete)
+ .isTrue()
+ }
+
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndMetadata_packageSourceUnspecified() {
+ // Unspecified is the default, so no need to explicitly set it
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(true)
+
+ clickPermissionRationaleContentInAppPermission()
+ assertPermissionRationaleDialogIsVisible(expected = true, showSettingsSection = false)
+ }
+
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndMetadata_packageSourceStore() {
+ installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(true)
+
+ clickPermissionRationaleContentInAppPermission()
+ assertPermissionRationaleDialogIsVisible(expected = true, showSettingsSection = false)
+ }
+
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndMetadata_packageSourceLocalFile() {
+ installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndMetadata_packageSourceDownloadedFile() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndMetadata_packageSourceOther() {
+ installPackageWithInstallSourceAndMetadataFromOther(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceUnspecified() {
+ // Unspecified is the default, so no need to explicitly set it
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31_WITH_ASL)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceStore() {
+ installPackageWithInstallSourceAndNoMetadataFromStore(APP_APK_NAME_31_WITH_ASL)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceLocalFile() {
+ installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_31_WITH_ASL)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceDownloadedFile() {
+ installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_31_WITH_ASL)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun showPermissionRationaleContainer_withInstallSourceAndNoMetadata_packageSourceOther() {
+ installPackageWithInstallSourceAndNoMetadataFromOther(APP_APK_NAME_31_WITH_ASL)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndNoMetadata() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndNullMetadata() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndEmptyMetadata() {
+ installPackageWithInstallSourceAndEmptyMetadata(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndInvalidMetadata() {
+ installPackageWithInstallSourceAndInvalidMetadata(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndMetadataWithoutTopLevelVersion() {
+ installPackageWithInstallSourceAndMetadataWithoutTopLevelVersion(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndMetadataWithInvalidTopLevelVersion() {
+ installPackageWithInstallSourceAndMetadataWithInvalidTopLevelVersion(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndMetadataWithoutSafetyLabelVersion() {
+ installPackageWithInstallSourceAndMetadataWithoutSafetyLabelVersion(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withInstallSourceAndMetadataWithInvalidSafetyLabelVersion() {
+ installPackageWithInstallSourceAndMetadataWithInvalidSafetyLabelVersion(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withOutInstallSource() {
+ installPackageWithoutInstallSource(APP_APK_PATH_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @Test
+ fun noShowPermissionRationaleContainer_withoutMetadata() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31)
+
+ navigateToIndividualPermissionSetting(ACCESS_COARSE_LOCATION)
+
+ assertAppPermissionRationaleContainerIsVisible(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun installFromTrustedSource_enabledAllowRadioButtonAndIfClickedAndChecked() {
+ installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST)
+
+ navigateToIndividualPermissionSetting(PHONE)
+
+ assertAllowButtonIsEnabledAndClickAndChecked()
+
+ pressBack()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun installFromDownloadedFile_disabledAllowRadioButtonAndIfClickedAndRestrictedSettingDialog_PhonePermGroup() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+
+ navigateToIndividualPermissionSetting(PHONE)
+
+ assertAllowButtonIsDisabledAndRestrictedSettingDialogPoppedUp()
+
+ pressBack()
+
+ pressBack()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun installFromDownloadedFile_disabledAllowRadioButtonAndIfClickedAndRestrictedSettingDialog_SMSPermGroup() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+
+ navigateToIndividualPermissionSetting(SMS)
+
+ assertAllowButtonIsDisabledAndRestrictedSettingDialogPoppedUp()
+
+ pressBack()
+
+ pressBack()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun installFromLocalFile_disabledAllowRadioButtonAndIfClickedAndRestrictedSettingDialog_PhonePermGroup() {
+ installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_LATEST)
+
+ navigateToIndividualPermissionSetting(PHONE)
+
+ assertAllowButtonIsDisabledAndRestrictedSettingDialogPoppedUp()
+
+ pressBack()
+
+ pressBack()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun installFromLocalFile_disabledAllowRadioButtonAndIfClickedAndRestrictedSettingDialog_SMSPermGroup() {
+ installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_LATEST)
+
+ navigateToIndividualPermissionSetting(SMS)
+
+ assertAllowButtonIsDisabledAndRestrictedSettingDialogPoppedUp()
+
+ pressBack()
+
+ pressBack()
+ }
+
+ private fun assertAllowButtonIsEnabledAndClickAndChecked() {
+ waitFindObject(By.res(ALLOW_RADIO_BUTTON).enabled(true).checked(false))
+ .click()
+ waitFindObject(By.res(ALLOW_RADIO_BUTTON).checked(true))
+ }
+
+ private fun assertAllowButtonIsDisabledAndRestrictedSettingDialogPoppedUp() {
+ waitFindObject(By.res(ALLOW_RADIO_BUTTON).enabled(false))
+ .clickAndWait(Until.newWindow(), TIMEOUT_MILLIS)
+
+ waitFindObject(ENHANCED_CONFIRMATION_DIALOG_SELECTOR, TIMEOUT_MILLIS)
+ }
+
+ private fun assertAppPermissionRationaleContainerIsVisible(expected: Boolean) {
+ findView(By.res(APP_PERMISSION_RATIONALE_CONTAINER_VIEW), expected)
+ }
+
+ companion object {
+ private const val PERMISSION_RATIONALE_ENABLED = "permission_rationale_enabled"
+ private val ENHANCED_CONFIRMATION_DIALOG_SELECTOR = By.res(
+ "com.android.permissioncontroller:id/enhanced_confirmation_dialog_title")
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt
new file mode 100644
index 000000000..17785a95b
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/BasePermissionTest.kt
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.app.Instrumentation
+import android.app.PendingIntent
+import android.app.PendingIntent.FLAG_MUTABLE
+import android.app.PendingIntent.FLAG_UPDATE_CURRENT
+import android.app.UiAutomation
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.RECEIVER_EXPORTED
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageInstaller.EXTRA_STATUS
+import android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE
+import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID
+import android.content.pm.PackageInstaller.STATUS_SUCCESS
+import android.content.pm.PackageInstaller.SessionParams
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.os.PersistableBundle
+import android.os.SystemClock
+import android.platform.test.rule.ScreenRecordRule
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.text.Html
+import android.util.Log
+import androidx.test.core.app.ActivityScenario
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.StaleObjectException
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.UiScrollable
+import androidx.test.uiautomator.UiSelector
+import androidx.test.uiautomator.Until
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.UiAutomatorUtils2
+import com.android.modules.utils.build.SdkLevel
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+import java.util.regex.Pattern
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Before
+import org.junit.Rule
+
+@ScreenRecordRule.ScreenRecord
+abstract class BasePermissionTest {
+ companion object {
+ private const val TAG = "BasePermissionTest"
+
+ private const val INSTALL_ACTION_CALLBACK = "BasePermissionTest.install_callback"
+
+ private const val MAX_SWIPES = 5
+
+ const val APK_DIRECTORY = "/data/local/tmp/cts-permissionui"
+
+ const val QUICK_CHECK_TIMEOUT_MILLIS = 100L
+ const val IDLE_TIMEOUT_MILLIS: Long = 1000
+ const val IDLE_LONG_TIMEOUT_MILLIS: Long = 5000
+ const val UNEXPECTED_TIMEOUT_MILLIS = 1000
+ const val TIMEOUT_MILLIS: Long = 20000
+ const val PACKAGE_INSTALLER_TIMEOUT = 60000L
+ const val NEW_WINDOW_TIMEOUT_MILLIS: Long = 20_000
+
+ @JvmStatic
+ protected val instrumentation: Instrumentation =
+ InstrumentationRegistry.getInstrumentation()
+ @JvmStatic protected val context: Context = instrumentation.context
+ @JvmStatic protected val uiAutomation: UiAutomation = instrumentation.uiAutomation
+ @JvmStatic protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+ @JvmStatic protected val packageManager: PackageManager = context.packageManager
+ private val packageInstaller = packageManager.packageInstaller
+ @JvmStatic
+ private val mPermissionControllerResources: Resources =
+ context
+ .createPackageContext(context.packageManager.permissionControllerPackageName, 0)
+ .resources
+
+ @JvmStatic
+ protected val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ @JvmStatic
+ protected val isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ @JvmStatic
+ protected val isAutomotive =
+ packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ @JvmStatic
+ protected val isAutomotiveSplitscreen = isAutomotive &&
+ packageManager.hasSystemFeature(
+ /* PackageManager.FEATURE_CAR_SPLITSCREEN_MULTITASKING */
+ "android.software.car.splitscreen_multitasking")
+ }
+
+ @get:Rule val screenRecordRule = ScreenRecordRule(false, false)
+
+ @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+ @get:Rule val freezeRotationRule = FreezeRotationRule()
+
+ var activityScenario: ActivityScenario<StartForFutureActivity>? = null
+
+ data class SessionResult(val status: Int?)
+
+ /** If a status was received the value of the status, otherwise null */
+ private var installSessionResult = LinkedBlockingQueue<SessionResult>()
+
+ private val installSessionResultReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID)
+ val msg = intent.getStringExtra(EXTRA_STATUS_MESSAGE)
+ Log.d(TAG, "status: $status, msg: $msg")
+
+ installSessionResult.offer(SessionResult(status))
+ }
+ }
+
+ private var screenTimeoutBeforeTest: Long = 0L
+
+ @Before
+ fun setUp() {
+ runWithShellPermissionIdentity {
+ screenTimeoutBeforeTest =
+ Settings.System.getLong(context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
+ Settings.System.putLong(
+ context.contentResolver,
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ 1800000L
+ )
+ }
+
+ uiDevice.wakeUp()
+ runShellCommand(instrumentation, "wm dismiss-keyguard")
+
+ uiDevice.findObject(By.text("Close"))?.click()
+ }
+
+ @Before
+ fun registerInstallSessionResultReceiver() {
+ context.registerReceiver(
+ installSessionResultReceiver,
+ IntentFilter(INSTALL_ACTION_CALLBACK),
+ RECEIVER_EXPORTED
+ )
+ }
+
+ @After
+ fun unregisterInstallSessionResultReceiver() {
+ try {
+ context.unregisterReceiver(installSessionResultReceiver)
+ } catch (ignored: IllegalArgumentException) {}
+ }
+
+ @After
+ fun tearDown() {
+ runWithShellPermissionIdentity {
+ Settings.System.putLong(
+ context.contentResolver,
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ screenTimeoutBeforeTest
+ )
+ }
+
+ try {
+ activityScenario?.close()
+ } catch (e: NullPointerException) {
+ // ignore
+ }
+
+ pressHome()
+ }
+
+ protected fun setDeviceConfigPrivacyProperty(
+ propertyName: String,
+ value: String,
+ ) {
+ runWithShellPermissionIdentity(instrumentation.uiAutomation) {
+ val valueWasSet =
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ /* name = */ propertyName,
+ /* value = */ value,
+ /* makeDefault = */ false
+ )
+ check(valueWasSet) { "Could not set $propertyName to $value" }
+ }
+ }
+
+ protected fun getPermissionControllerString(res: String, vararg formatArgs: Any): Pattern {
+ val textWithHtml =
+ mPermissionControllerResources.getString(
+ mPermissionControllerResources.getIdentifier(
+ res,
+ "string",
+ "com.android.permissioncontroller"
+ ),
+ *formatArgs
+ )
+ val textWithoutHtml = Html.fromHtml(textWithHtml, 0).toString()
+ return Pattern.compile(
+ Pattern.quote(textWithoutHtml),
+ Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE
+ )
+ }
+
+ protected fun getPermissionControllerResString(res: String): String? {
+ try {
+ return mPermissionControllerResources.getString(
+ mPermissionControllerResources.getIdentifier(
+ res,
+ "string",
+ "com.android.permissioncontroller"
+ )
+ )
+ } catch (e: Resources.NotFoundException) {
+ return null
+ }
+ }
+
+ protected fun byAnyText(vararg texts: String?): BySelector {
+ var regex = ""
+ for (text in texts) {
+ if (text != null) {
+ regex = regex + Pattern.quote(text) + "|"
+ }
+ }
+ if (regex.endsWith("|")) {
+ regex = regex.dropLast(1)
+ }
+ return By.text(Pattern.compile(regex, Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE))
+ }
+
+ protected open fun installPackage(
+ apkPath: String,
+ reinstall: Boolean = false,
+ grantRuntimePermissions: Boolean = false,
+ expectSuccess: Boolean = true,
+ installSource: String? = null
+ ) {
+ val output =
+ runShellCommandOrThrow(
+ "pm install${if (SdkLevel.isAtLeastU()) " --bypass-low-target-sdk-block" else ""} " +
+ "${if (reinstall) " -r" else ""}${if (grantRuntimePermissions) " -g"
+ else ""}${if (installSource != null) " -i $installSource" else ""} $apkPath"
+ )
+ .trim()
+ if (expectSuccess) {
+ assertEquals("Success", output)
+ } else {
+ assertNotEquals("Success", output)
+ }
+ }
+
+ protected fun installPackageViaSession(
+ apkName: String,
+ appMetadata: PersistableBundle? = null,
+ packageSource: Int? = null,
+ allowlistedRestrictedPermissions: Set<String>? = null
+ ) {
+ val (sessionId, session) = createPackageInstallerSession(
+ packageSource,
+ allowlistedRestrictedPermissions
+ )
+ runWithShellPermissionIdentity {
+ writePackageInstallerSession(session, apkName)
+ if (appMetadata != null) {
+ setAppMetadata(session, appMetadata)
+ }
+ commitPackageInstallerSession(session)
+
+ // No need to click installer UI here due to running in shell permission identity and
+ // not needing user interaciton to complete install. Install should have succeeded.
+ val result = getInstallSessionResult()
+ assertThat(result.status).isEqualTo(STATUS_SUCCESS)
+ }
+ }
+
+ protected fun uninstallPackage(packageName: String, requireSuccess: Boolean = true) {
+ val output = runShellCommand("pm uninstall $packageName").trim()
+ if (requireSuccess) {
+ assertEquals("Success", output)
+ }
+ }
+
+ protected fun waitFindObject(selector: BySelector): UiObject2 {
+ return findObjectWithRetry({ t -> UiAutomatorUtils2.waitFindObject(selector, t) })!!
+ }
+
+ protected fun waitFindObject(selector: BySelector, timeoutMillis: Long): UiObject2 {
+ return findObjectWithRetry(
+ { t -> UiAutomatorUtils2.waitFindObject(selector, t) },
+ timeoutMillis
+ )!!
+ }
+
+ protected fun waitFindObjectOrNull(selector: BySelector): UiObject2? {
+ return findObjectWithRetry({ t -> UiAutomatorUtils2.waitFindObjectOrNull(selector, t) })
+ }
+
+ protected fun waitFindObjectOrNull(selector: BySelector, timeoutMillis: Long): UiObject2? {
+ return findObjectWithRetry(
+ { t -> UiAutomatorUtils2.waitFindObjectOrNull(selector, t) },
+ timeoutMillis
+ )
+ }
+
+ private fun findObjectWithRetry(
+ automatorMethod: (timeoutMillis: Long) -> UiObject2?,
+ timeoutMillis: Long = 20_000L
+ ): UiObject2? {
+ val startTime = SystemClock.elapsedRealtime()
+ return try {
+ automatorMethod(timeoutMillis)
+ } catch (e: StaleObjectException) {
+ val remainingTime = timeoutMillis - (SystemClock.elapsedRealtime() - startTime)
+ if (remainingTime <= 0) {
+ throw e
+ }
+ automatorMethod(remainingTime)
+ }
+ }
+
+ protected fun click(selector: BySelector, timeoutMillis: Long = 20_000) {
+ waitFindObject(selector, timeoutMillis).click()
+ }
+
+ protected fun clickAndWaitForWindowTransition(
+ selector: BySelector,
+ timeoutMillis: Long = 20_000
+ ) {
+ waitFindObject(selector, timeoutMillis)
+ .clickAndWait(Until.newWindow(), NEW_WINDOW_TIMEOUT_MILLIS)
+ }
+
+ protected fun findView(selector: BySelector, expected: Boolean) {
+ val timeoutMs =
+ if (expected) {
+ 10000L
+ } else {
+ 1000L
+ }
+
+ val exception =
+ try {
+ waitFindObject(selector, timeoutMs)
+ null
+ } catch (e: Exception) {
+ e
+ }
+ Assert.assertTrue("Expected to find view: $expected", (exception == null) == expected)
+ }
+
+ protected fun clickPermissionControllerUi(selector: BySelector, timeoutMillis: Long = 20_000) {
+ click(selector.pkg(context.packageManager.permissionControllerPackageName), timeoutMillis)
+ }
+
+ /**
+ * Clicks Permission Controller UI with a swipe based timeout instead of a time based one
+ *
+ * Use this if finding some Permission Controller UI isn't time bound.
+ *
+ * @param text The text to search for
+ * @param maxSearchSwipes See {@link UiScrollable#setMaxSearchSwipes}
+ */
+ protected fun clickPermissionControllerUi(text: String, maxSearchSwipes: Int = 5) {
+ scrollToText(text, maxSearchSwipes).click()
+ }
+
+ private fun scrollToText(text: String, maxSearchSwipes: Int = MAX_SWIPES): UiObject2 {
+ val scrollable =
+ UiScrollable(UiSelector().scrollable(true)).apply {
+ this.maxSearchSwipes = maxSearchSwipes
+ }
+
+ scrollable.scrollTextIntoView(text)
+
+ val foundObject =
+ uiDevice.findObject(
+ By.text(text).pkg(context.packageManager.permissionControllerPackageName)
+ )
+ Assert.assertNotNull("View not found after scrolling", foundObject)
+
+ return foundObject
+ }
+
+ protected fun pressBack() {
+ uiDevice.pressBack()
+ }
+
+ protected fun pressHome() {
+ uiDevice.pressHome()
+ }
+
+ protected fun pressDPadDown() {
+ uiDevice.pressDPadDown()
+ waitForIdle()
+ }
+
+ protected fun waitForIdle() = uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+
+ protected fun waitForIdleLong() =
+ uiAutomation.waitForIdle(IDLE_LONG_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
+
+ protected fun startActivityForFuture(
+ intent: Intent
+ ): CompletableFuture<Instrumentation.ActivityResult> =
+ CompletableFuture<Instrumentation.ActivityResult>().also {
+ activityScenario =
+ ActivityScenario.launch(StartForFutureActivity::class.java).onActivity { activity ->
+ activity.startActivityForFuture(intent, it)
+ }
+ }
+
+ open fun enableComponent(component: ComponentName) {
+ packageManager.setComponentEnabledSetting(
+ component,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP
+ )
+ }
+
+ open fun disableComponent(component: ComponentName) {
+ packageManager.setComponentEnabledSetting(
+ component,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP
+ )
+ }
+
+ private fun createPackageInstallerSession(
+ packageSource: Int? = null,
+ allowlistedRestrictedPermissions: Set<String>? = null
+ ): Pair<Int, PackageInstaller.Session> {
+ // Create session
+ val sessionParam = SessionParams(SessionParams.MODE_FULL_INSTALL)
+ allowlistedRestrictedPermissions?.let {
+ sessionParam.setWhitelistedRestrictedPermissions(it)
+ }
+
+ if (packageSource != null) {
+ sessionParam.setPackageSource(packageSource)
+ }
+
+ val sessionId = packageInstaller.createSession(sessionParam)
+ val session = packageInstaller.openSession(sessionId)!!
+
+ return Pair(sessionId, session)
+ }
+
+ private fun writePackageInstallerSession(session: PackageInstaller.Session, apkName: String) {
+ val apkFile = File(APK_DIRECTORY, apkName)
+ // Write data to session
+ apkFile.inputStream().use { fileOnDisk ->
+ session
+ .openWrite(/* name= */ apkName, /* offsetBytes= */ 0, /* lengthBytes= */ -1)
+ .use { sessionFile -> fileOnDisk.copyTo(sessionFile) }
+ }
+ }
+
+ private fun commitPackageInstallerSession(session: PackageInstaller.Session) {
+ // PendingIntent that triggers a INSTALL_ACTION_CALLBACK broadcast that gets received by
+ // installSessionResultReceiver when install actions occur with this session
+ val installActionPendingIntent =
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ Intent(INSTALL_ACTION_CALLBACK).setPackage(context.packageName),
+ FLAG_UPDATE_CURRENT or FLAG_MUTABLE
+ )
+ session.commit(installActionPendingIntent.intentSender)
+ }
+
+ private fun setAppMetadata(session: PackageInstaller.Session, data: PersistableBundle) {
+ try {
+ session.setAppMetadata(data)
+ } catch (e: Exception) {
+ session.abandon()
+ throw e
+ }
+ }
+
+ /** Wait for session's install result and return it */
+ private fun getInstallSessionResult(timeout: Long = PACKAGE_INSTALLER_TIMEOUT): SessionResult {
+ return installSessionResult.poll(timeout, TimeUnit.MILLISECONDS)
+ ?: SessionResult(null /* status */)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
new file mode 100644
index 000000000..244d18ca6
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt
@@ -0,0 +1,1454 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest
+import android.app.Activity
+import android.app.ActivityManager
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Intent
+import android.content.Intent.ACTION_REVIEW_APP_DATA_SHARING_UPDATES
+import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_OTHER
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_STORE
+import android.content.pm.PackageInstaller.SessionParams
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Build
+import android.os.Process
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.text.Spanned
+import android.text.style.ClickableSpan
+import android.view.View
+import android.view.accessibility.AccessibilityNodeInfo
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.StaleObjectException
+import androidx.test.uiautomator.UiObjectNotFoundException
+import androidx.test.uiautomator.UiScrollable
+import androidx.test.uiautomator.UiSelector
+import androidx.test.uiautomator.Until
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.UiAutomatorUtils2
+import com.android.modules.utils.build.SdkLevel
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.regex.Pattern
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+
+abstract class BaseUsePermissionTest : BasePermissionTest() {
+ companion object {
+ const val APP_APK_NAME_31 = "CtsUsePermissionApp31.apk"
+ const val APP_APK_NAME_31_WITH_ASL = "CtsUsePermissionApp31WithAsl.apk"
+ const val APP_APK_NAME_LATEST = "CtsUsePermissionAppLatest.apk"
+
+ const val APP_APK_PATH_22 = "$APK_DIRECTORY/CtsUsePermissionApp22.apk"
+ const val APP_APK_PATH_22_CALENDAR_ONLY =
+ "$APK_DIRECTORY/CtsUsePermissionApp22CalendarOnly.apk"
+ const val APP_APK_PATH_22_NONE = "$APK_DIRECTORY/CtsUsePermissionApp22None.apk"
+ const val APP_APK_PATH_23 = "$APK_DIRECTORY/CtsUsePermissionApp23.apk"
+ const val APP_APK_PATH_25 = "$APK_DIRECTORY/CtsUsePermissionApp25.apk"
+ const val APP_APK_PATH_26 = "$APK_DIRECTORY/CtsUsePermissionApp26.apk"
+ const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsUsePermissionApp28.apk"
+ const val APP_APK_PATH_29 = "$APK_DIRECTORY/CtsUsePermissionApp29.apk"
+ const val APP_APK_PATH_30 = "$APK_DIRECTORY/CtsUsePermissionApp30.apk"
+ const val APP_APK_PATH_31 = "$APK_DIRECTORY/$APP_APK_NAME_31"
+ const val APP_APK_PATH_32 = "$APK_DIRECTORY/CtsUsePermissionApp32.apk"
+
+ const val APP_APK_PATH_30_WITH_BACKGROUND =
+ "$APK_DIRECTORY/CtsUsePermissionApp30WithBackground.apk"
+ const val APP_APK_PATH_30_WITH_BLUETOOTH =
+ "$APK_DIRECTORY/CtsUsePermissionApp30WithBluetooth.apk"
+ const val APP_APK_PATH_LATEST = "$APK_DIRECTORY/CtsUsePermissionAppLatest.apk"
+ const val APP_APK_PATH_LATEST_NONE = "$APK_DIRECTORY/CtsUsePermissionAppLatestNone.apk"
+ const val APP_APK_PATH_WITH_OVERLAY = "$APK_DIRECTORY/CtsUsePermissionAppWithOverlay.apk"
+ const val APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31 =
+ "$APK_DIRECTORY/CtsCreateNotificationChannelsApp31.apk"
+ const val APP_APK_PATH_MEDIA_PERMISSION_33_WITH_STORAGE =
+ "$APK_DIRECTORY/CtsMediaPermissionApp33WithStorage.apk"
+ const val APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE =
+ "$APK_DIRECTORY/CtsUsePermissionAppImplicitUserSelectStorage.apk"
+ const val APP_APK_PATH_STORAGE_33 = "$APK_DIRECTORY/CtsUsePermissionAppStorage33.apk"
+ const val APP_APK_PATH_OTHER_APP = "$APK_DIRECTORY/CtsDifferentPkgNameApp.apk"
+ const val APP_APK_PATH_TWO_PERM_REQUESTS =
+ "$APK_DIRECTORY/CtsAppThatMakesTwoPermRequests.apk"
+ const val APP_PACKAGE_NAME = "android.permissionui.cts.usepermission"
+ const val OTHER_APP_PACKAGE_NAME = "android.permissionui.cts.usepermissionother"
+ const val TEST_INSTALLER_PACKAGE_NAME = "android.permissionui.cts"
+
+ const val ALLOW_ALL_BUTTON =
+ "com.android.permissioncontroller:id/permission_allow_all_button"
+ const val SELECT_BUTTON =
+ "com.android.permissioncontroller:id/permission_allow_selected_button"
+ const val DONT_SELECT_MORE_BUTTON =
+ "com.android.permissioncontroller:id/permission_dont_allow_more_selected_button"
+ const val ALLOW_BUTTON = "com.android.permissioncontroller:id/permission_allow_button"
+ const val ALLOW_FOREGROUND_BUTTON =
+ "com.android.permissioncontroller:id/permission_allow_foreground_only_button"
+ const val DENY_BUTTON = "com.android.permissioncontroller:id/permission_deny_button"
+ const val DENY_AND_DONT_ASK_AGAIN_BUTTON =
+ "com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button"
+ const val NO_UPGRADE_BUTTON =
+ "com.android.permissioncontroller:id/permission_no_upgrade_button"
+ const val NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON =
+ "com.android.permissioncontroller:" +
+ "id/permission_no_upgrade_and_dont_ask_again_button"
+
+ const val ALLOW_ALWAYS_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/allow_always_radio_button"
+ const val ALLOW_RADIO_BUTTON_FRAME =
+ "com.android.permissioncontroller:id/allow_radio_button_frame"
+ const val ALLOW_RADIO_BUTTON = "com.android.permissioncontroller:id/allow_radio_button"
+ const val ALLOW_FOREGROUND_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/allow_foreground_only_radio_button"
+ const val ASK_RADIO_BUTTON = "com.android.permissioncontroller:id/ask_radio_button"
+ const val DENY_RADIO_BUTTON = "com.android.permissioncontroller:id/deny_radio_button"
+ const val SELECT_RADIO_BUTTON = "com.android.permissioncontroller:id/select_radio_button"
+ const val EDIT_PHOTOS_BUTTON = "com.android.permissioncontroller:id/edit_selected_button"
+
+ const val NOTIF_TEXT = "permgrouprequest_notifications"
+ const val ALLOW_BUTTON_TEXT = "grant_dialog_button_allow"
+ const val ALLOW_ALL_FILES_BUTTON_TEXT = "app_permission_button_allow_all_files"
+ const val ALLOW_FOREGROUND_BUTTON_TEXT = "grant_dialog_button_allow_foreground"
+ const val ALLOW_FOREGROUND_PREFERENCE_TEXT = "permission_access_only_foreground"
+ const val ASK_BUTTON_TEXT = "app_permission_button_ask"
+ const val ALLOW_ONE_TIME_BUTTON_TEXT = "grant_dialog_button_allow_one_time"
+ const val DENY_BUTTON_TEXT = "grant_dialog_button_deny"
+ const val DENY_ANYWAY_BUTTON_TEXT = "grant_dialog_button_deny_anyway"
+ const val DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT =
+ "grant_dialog_button_deny_and_dont_ask_again"
+ const val NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT = "grant_dialog_button_no_upgrade"
+ const val ALERT_DIALOG_MESSAGE = "android:id/message"
+ const val ALERT_DIALOG_OK_BUTTON = "android:id/button1"
+ const val APP_PERMISSION_RATIONALE_CONTAINER_VIEW =
+ "com.android.permissioncontroller:id/app_permission_rationale_container"
+ const val APP_PERMISSION_RATIONALE_CONTENT_VIEW =
+ "com.android.permissioncontroller:id/app_permission_rationale_content"
+ const val GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW =
+ "com.android.permissioncontroller:id/permission_rationale_container"
+ const val PERMISSION_RATIONALE_ACTIVITY_TITLE_VIEW =
+ "com.android.permissioncontroller:id/permission_rationale_title"
+ const val DATA_SHARING_SOURCE_TITLE_ID =
+ "com.android.permissioncontroller:id/data_sharing_source_title"
+ const val DATA_SHARING_SOURCE_MESSAGE_ID =
+ "com.android.permissioncontroller:id/data_sharing_source_message"
+ const val PURPOSE_TITLE_ID = "com.android.permissioncontroller:id/purpose_title"
+ const val PURPOSE_MESSAGE_ID = "com.android.permissioncontroller:id/purpose_message"
+ const val LEARN_MORE_TITLE_ID = "com.android.permissioncontroller:id/learn_more_title"
+ const val LEARN_MORE_MESSAGE_ID = "com.android.permissioncontroller:id/learn_more_message"
+ const val DETAIL_MESSAGE_ID = "com.android.permissioncontroller:id/detail_message"
+ const val PERMISSION_RATIONALE_SETTINGS_SECTION =
+ "com.android.permissioncontroller:id/settings_section"
+ const val SETTINGS_TITLE_ID = "com.android.permissioncontroller:id/settings_title"
+ const val SETTINGS_MESSAGE_ID = "com.android.permissioncontroller:id/settings_message"
+
+ const val REQUEST_LOCATION_MESSAGE = "permgrouprequest_location"
+
+ const val DATA_SHARING_UPDATES = "Data sharing updates for location"
+ const val DATA_SHARING_UPDATES_SUBTITLE =
+ "These apps have changed the way they may share your location data. They may not" +
+ " have shared it before, or may now share it for advertising or marketing" +
+ " purposes."
+ const val DATA_SHARING_NO_UPDATES_MESSAGE = "No updates at this time"
+ const val UPDATES_IN_LAST_30_DAYS = "Updated within 30 days"
+ const val DATA_SHARING_UPDATES_FOOTER_MESSAGE =
+ "The developers of these apps provided info about their data sharing practices" +
+ " to an app store. They may update it over time.\n\nData sharing" +
+ " practices may vary based on your app version, use, region, and age."
+ const val LEARN_ABOUT_DATA_SHARING = "Learn about data sharing"
+ const val LOCATION_PERMISSION = "Location permission"
+ const val APP_PACKAGE_NAME_SUBSTRING = "android.permissionui"
+ const val NOW_SHARED_WITH_THIRD_PARTIES =
+ "Your location data is now shared with third " + "parties"
+ const val NOW_SHARED_WITH_THIRD_PARTIES_FOR_ADS =
+ "Your location data is now shared with " + "third parties for advertising or marketing"
+ const val PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS = "data_sharing_update_period_millis"
+ const val PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP =
+ "max_safety_labels_persisted_per_app"
+
+ // The highest SDK for which the system will show a "low SDK" warning when launching the app
+ const val MAX_SDK_FOR_SDK_WARNING = 27
+ const val MIN_SDK_FOR_RUNTIME_PERMS = 23
+
+ val TEST_INSTALLER_ACTIVITY_COMPONENT_NAME =
+ ComponentName(context, TestInstallerActivity::class.java)
+
+ val MEDIA_PERMISSIONS: Set<String> =
+ mutableSetOf(
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
+ Manifest.permission.READ_MEDIA_AUDIO,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ )
+ .apply {
+ if (SdkLevel.isAtLeastU()) {
+ add(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
+ }
+ }
+ .toSet()
+
+ val STORAGE_AND_MEDIA_PERMISSIONS =
+ MEDIA_PERMISSIONS.plus(Manifest.permission.READ_EXTERNAL_STORAGE)
+ .plus(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+
+ @JvmStatic protected val PICKER_ENABLED_SETTING = "photo_picker_prompt_enabled"
+
+ @JvmStatic
+ protected fun isPhotoPickerPermissionPromptEnabled(): Boolean {
+ return SdkLevel.isAtLeastU() &&
+ !isTv &&
+ !isAutomotive &&
+ !isWatch &&
+ callWithShellPermissionIdentity {
+ DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PICKER_ENABLED_SETTING,
+ true
+ )
+ }
+ }
+ }
+
+ enum class PermissionState {
+ ALLOWED,
+ DENIED,
+ DENIED_WITH_PREJUDICE
+ }
+
+ private val platformResources = context.createPackageContext("android", 0).resources
+ private val permissionToLabelResNameMap =
+ mapOf(
+ // Contacts
+ android.Manifest.permission.READ_CONTACTS to "@android:string/permgrouplab_contacts",
+ android.Manifest.permission.WRITE_CONTACTS to "@android:string/permgrouplab_contacts",
+ // Calendar
+ android.Manifest.permission.READ_CALENDAR to "@android:string/permgrouplab_calendar",
+ android.Manifest.permission.WRITE_CALENDAR to "@android:string/permgrouplab_calendar",
+ // SMS
+ android.Manifest.permission_group.SMS to "@android:string/permgrouplab_sms",
+ android.Manifest.permission.SEND_SMS to "@android:string/permgrouplab_sms",
+ android.Manifest.permission.RECEIVE_SMS to "@android:string/permgrouplab_sms",
+ android.Manifest.permission.READ_SMS to "@android:string/permgrouplab_sms",
+ android.Manifest.permission.RECEIVE_WAP_PUSH to "@android:string/permgrouplab_sms",
+ android.Manifest.permission.RECEIVE_MMS to "@android:string/permgrouplab_sms",
+ "android.permission.READ_CELL_BROADCASTS" to "@android:string/permgrouplab_sms",
+ // Storage
+ android.Manifest.permission.READ_EXTERNAL_STORAGE to
+ "@android:string/permgrouplab_storage",
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE to
+ "@android:string/permgrouplab_storage",
+ // Location
+ android.Manifest.permission.ACCESS_FINE_LOCATION to
+ "@android:string/permgrouplab_location",
+ android.Manifest.permission.ACCESS_COARSE_LOCATION to
+ "@android:string/permgrouplab_location",
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to
+ "@android:string/permgrouplab_location",
+ // Phone
+ android.Manifest.permission_group.PHONE to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.READ_PHONE_STATE to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.CALL_PHONE to "@android:string/permgrouplab_phone",
+ "android.permission.ACCESS_IMS_CALL_SERVICE" to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.READ_CALL_LOG to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.WRITE_CALL_LOG to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.ADD_VOICEMAIL to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.USE_SIP to "@android:string/permgrouplab_phone",
+ android.Manifest.permission.PROCESS_OUTGOING_CALLS to
+ "@android:string/permgrouplab_phone",
+ // Microphone
+ android.Manifest.permission.RECORD_AUDIO to "@android:string/permgrouplab_microphone",
+ // Camera
+ android.Manifest.permission.CAMERA to "@android:string/permgrouplab_camera",
+ // Body sensors
+ android.Manifest.permission.BODY_SENSORS to "@android:string/permgrouplab_sensors",
+ android.Manifest.permission.BODY_SENSORS_BACKGROUND to
+ "@android:string/permgrouplab_sensors",
+ // Bluetooth
+ android.Manifest.permission.BLUETOOTH_CONNECT to
+ "@android:string/permgrouplab_nearby_devices",
+ android.Manifest.permission.BLUETOOTH_SCAN to
+ "@android:string/permgrouplab_nearby_devices",
+ // Aural
+ android.Manifest.permission.READ_MEDIA_AUDIO to
+ "@android:string/permgrouplab_readMediaAural",
+ // Visual
+ android.Manifest.permission.READ_MEDIA_IMAGES to
+ "@android:string/permgrouplab_readMediaVisual",
+ android.Manifest.permission.READ_MEDIA_VIDEO to
+ "@android:string/permgrouplab_readMediaVisual"
+ )
+
+ @Before
+ @After
+ fun uninstallApp() {
+ uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false)
+ }
+
+ override fun installPackage(
+ apkPath: String,
+ reinstall: Boolean,
+ grantRuntimePermissions: Boolean,
+ expectSuccess: Boolean,
+ installSource: String?
+ ) {
+ installPackage(
+ apkPath,
+ reinstall,
+ grantRuntimePermissions,
+ expectSuccess,
+ installSource,
+ false
+ )
+ }
+
+ fun installPackage(
+ apkPath: String,
+ reinstall: Boolean = false,
+ grantRuntimePermissions: Boolean = false,
+ expectSuccess: Boolean = true,
+ installSource: String? = null,
+ skipClearLowSdkDialog: Boolean = false
+ ) {
+ super.installPackage(
+ apkPath,
+ reinstall,
+ grantRuntimePermissions,
+ expectSuccess,
+ installSource
+ )
+
+ val targetSdk = getTargetSdk()
+ // If the targetSDK is high enough, the low sdk warning won't show. If the SDK is
+ // below runtime permissions, the dialog will be delayed by the permission review screen.
+ // If success is not expected, don't bother trying
+ if (
+ targetSdk > MAX_SDK_FOR_SDK_WARNING ||
+ targetSdk < MIN_SDK_FOR_RUNTIME_PERMS ||
+ !expectSuccess ||
+ skipClearLowSdkDialog
+ ) {
+ return
+ }
+
+ val finishOnCreateIntent =
+ Intent().apply {
+ component =
+ ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.FinishOnCreateActivity")
+ flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
+ }
+
+ // Check if an activity resolves for the test app. If it doesn't, then our test app doesn't
+ // have the usual set of activities, and likely won't be opened, and thus, won't show the
+ // dialog
+ callWithShellPermissionIdentity {
+ context.packageManager.resolveActivity(finishOnCreateIntent, PackageManager.MATCH_ALL)
+ }
+ ?: return
+
+ // Start the test app, and expect the targetSDK warning dialog
+ context.startActivity(finishOnCreateIntent)
+ clearTargetSdkWarning()
+ // Kill the test app, so that the next time we launch, we don't see the app warning dialog
+ killTestApp()
+ }
+
+ protected fun clearTargetSdkWarning(timeoutMillis: Long = TIMEOUT_MILLIS) {
+ if (SdkLevel.isAtLeastV()) {
+ // In V and above, the target SDK dialog can be disabled via system property
+ return
+ }
+
+ val targetSdkWarningVisible =
+ uiDevice.wait(
+ Until.hasObject(
+ By.textStartsWith("This app was built for an older version of Android")
+ ),
+ timeoutMillis
+ )
+ if (targetSdkWarningVisible) {
+ try {
+ uiDevice.findObject(By.res("android:id/button1")).click()
+ } catch (e: StaleObjectException) {
+ // Click sometimes fails with StaleObjectException (b/280430717).
+ e.printStackTrace()
+ }
+ }
+ }
+
+ protected fun killTestApp() {
+ pressBack()
+ pressBack()
+ runWithShellPermissionIdentity {
+ val am = context.getSystemService(ActivityManager::class.java)!!
+ am.forceStopPackage(APP_PACKAGE_NAME)
+ }
+ waitForIdle()
+ }
+
+ protected fun clickPermissionReviewContinue() {
+ if (isAutomotive || isWatch) {
+ clickAndWaitForWindowTransition(
+ By.text(getPermissionControllerString("review_button_continue")),
+ TIMEOUT_MILLIS * 2
+ )
+ } else {
+ clickAndWaitForWindowTransition(
+ By.res("com.android.permissioncontroller:id/continue_button")
+ )
+ }
+ }
+
+ protected fun clickPermissionReviewContinueAndClearSdkWarning() {
+ clickPermissionReviewContinue()
+ clearTargetSdkWarning()
+ }
+
+ protected fun installPackageWithInstallSourceAndEmptyMetadata(apkName: String) {
+ installPackageViaSession(apkName, AppMetadata.createEmptyAppMetadata())
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadata(apkName: String) {
+ installPackageViaSession(apkName, AppMetadata.createDefaultAppMetadata())
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataFromStore(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createDefaultAppMetadata(),
+ PACKAGE_SOURCE_STORE
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataFromLocalFile(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createDefaultAppMetadata(),
+ PACKAGE_SOURCE_LOCAL_FILE
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataFromDownloadedFile(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createDefaultAppMetadata(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ }
+
+ protected fun installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ apkName: String
+ ) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createDefaultAppMetadata(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE,
+ allowlistedRestrictedPermissions = SessionParams.RESTRICTED_PERMISSIONS_ALL
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataFromOther(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createDefaultAppMetadata(),
+ PACKAGE_SOURCE_OTHER
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndNoMetadata(apkName: String) {
+ installPackageViaSession(apkName)
+ }
+
+ protected fun installPackageWithInstallSourceAndNoMetadataFromStore(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ packageSource = PACKAGE_SOURCE_STORE
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndNoMetadataFromLocalFile(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ packageSource = PACKAGE_SOURCE_LOCAL_FILE
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndNoMetadataFromOther(apkName: String) {
+ installPackageViaSession(
+ apkName,
+ packageSource = PACKAGE_SOURCE_OTHER
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndInvalidMetadata(apkName: String) {
+ installPackageViaSession(apkName, AppMetadata.createInvalidAppMetadata())
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataWithoutTopLevelVersion(
+ apkName: String
+ ) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createInvalidAppMetadataWithoutTopLevelVersion()
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataWithInvalidTopLevelVersion(
+ apkName: String
+ ) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createInvalidAppMetadataWithInvalidTopLevelVersion()
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataWithoutSafetyLabelVersion(
+ apkName: String
+ ) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createInvalidAppMetadataWithoutSafetyLabelVersion()
+ )
+ }
+
+ protected fun installPackageWithInstallSourceAndMetadataWithInvalidSafetyLabelVersion(
+ apkName: String
+ ) {
+ installPackageViaSession(
+ apkName,
+ AppMetadata.createInvalidAppMetadataWithInvalidSafetyLabelVersion()
+ )
+ }
+
+ protected fun installPackageWithoutInstallSource(apkName: String) {
+ // TODO(b/257293222): Update/remove when hooking up PackageManager APIs
+ installPackage(apkName)
+ }
+
+ protected fun assertPermissionRationaleActivityTitleIsVisible(expected: Boolean) {
+ findView(By.res(PERMISSION_RATIONALE_ACTIVITY_TITLE_VIEW), expected = expected)
+ }
+
+ protected fun assertPermissionRationaleActivityDataSharingSourceSectionVisible(
+ expected: Boolean
+ ) {
+ findView(By.res(DATA_SHARING_SOURCE_TITLE_ID), expected = expected)
+ findView(By.res(DATA_SHARING_SOURCE_MESSAGE_ID), expected = expected)
+ }
+
+ protected fun assertPermissionRationaleActivityPurposeSectionVisible(expected: Boolean) {
+ findView(By.res(PURPOSE_TITLE_ID), expected = expected)
+ findView(By.res(PURPOSE_MESSAGE_ID), expected = expected)
+ }
+
+ protected fun assertPermissionRationaleActivityLearnMoreSectionVisible(expected: Boolean) {
+ findView(By.res(LEARN_MORE_TITLE_ID), expected = expected)
+ findView(By.res(LEARN_MORE_MESSAGE_ID), expected = expected)
+ }
+
+ protected fun assertPermissionRationaleActivitySettingsSectionVisible(expected: Boolean) {
+ findView(By.res(PERMISSION_RATIONALE_SETTINGS_SECTION), expected = expected)
+ findView(By.res(SETTINGS_TITLE_ID), expected = expected)
+ findView(By.res(SETTINGS_MESSAGE_ID), expected = expected)
+ }
+
+ protected fun assertPermissionRationaleDialogIsVisible(
+ expected: Boolean,
+ showSettingsSection: Boolean = true
+ ) {
+ assertPermissionRationaleActivityTitleIsVisible(expected)
+ assertPermissionRationaleActivityDataSharingSourceSectionVisible(expected)
+ assertPermissionRationaleActivityPurposeSectionVisible(expected)
+ assertPermissionRationaleActivityLearnMoreSectionVisible(expected)
+ if (expected) {
+ assertPermissionRationaleActivitySettingsSectionVisible(showSettingsSection)
+ }
+ }
+
+ protected fun assertPermissionRationaleContainerOnGrantDialogIsVisible(expected: Boolean) {
+ findView(By.res(GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW), expected = expected)
+ }
+
+ protected fun clickPermissionReviewCancel() {
+ if (isAutomotive || isWatch) {
+ clickAndWaitForWindowTransition(
+ By.text(getPermissionControllerString("review_button_cancel"))
+ )
+ } else {
+ clickAndWaitForWindowTransition(
+ By.res("com.android.permissioncontroller:id/cancel_button")
+ )
+ }
+ }
+
+ protected fun approvePermissionReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
+ clickPermissionReviewContinueAndClearSdkWarning()
+ }
+ }
+
+ protected fun cancelPermissionReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_CANCELED) {
+ clickPermissionReviewCancel()
+ }
+ }
+
+ protected fun assertAppDoesNotNeedPermissionReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {}
+ }
+
+ protected inline fun startAppActivityAndAssertResultCode(
+ expectedResultCode: Int,
+ block: () -> Unit
+ ) {
+ val future =
+ startActivityForFuture(
+ Intent().apply {
+ component =
+ ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.FinishOnCreateActivity")
+ }
+ )
+ block()
+ assertEquals(
+ expectedResultCode,
+ future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).resultCode
+ )
+ }
+
+ protected inline fun requestAppPermissionsForNoResult(
+ vararg permissions: String?,
+ crossinline block: () -> Unit
+ ) {
+ // Request the permissions
+ doAndWaitForWindowTransition {
+ context.startActivity(
+ Intent().apply {
+ component =
+ ComponentName(
+ APP_PACKAGE_NAME,
+ "$APP_PACKAGE_NAME.RequestPermissionsActivity"
+ )
+ putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions)
+ addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ )
+ }
+ // Perform the post-request action
+ block()
+ }
+
+ protected inline fun requestAppPermissions(
+ vararg permissions: String?,
+ askTwice: Boolean = false,
+ waitForWindowTransition: Boolean = !isWatch,
+ crossinline block: () -> Unit
+ ): Instrumentation.ActivityResult {
+ // Request the permissions
+ lateinit var future: CompletableFuture<Instrumentation.ActivityResult>
+ doAndWaitForWindowTransition {
+ future =
+ startActivityForFuture(
+ Intent().apply {
+ component =
+ ComponentName(
+ APP_PACKAGE_NAME,
+ "$APP_PACKAGE_NAME.RequestPermissionsActivity"
+ )
+ putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions)
+ putExtra("$APP_PACKAGE_NAME.ASK_TWICE", askTwice)
+ }
+ )
+ }
+
+ // Notification permission prompt is shown first, so get it out of the way
+ clickNotificationPermissionRequestAllowButtonIfAvailable()
+ // Perform the post-request action
+ if (waitForWindowTransition) {
+ doAndWaitForWindowTransition { block() }
+ } else {
+ block()
+ }
+ return future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ }
+
+ protected inline fun requestAppPermissionsAndAssertResult(
+ permissions: Array<out String?>,
+ permissionAndExpectedGrantResults: Array<out Pair<String?, Boolean>>,
+ askTwice: Boolean = false,
+ waitForWindowTransition: Boolean = !isWatch,
+ crossinline block: () -> Unit
+ ) {
+ var shouldWaitForWindowTransition = waitForWindowTransition
+ // Do not wait for windowTransition after action is performed on auto, when permissions
+ // are being denied. The click deny function explicitly waits for window to transition
+ if (isAutomotive) {
+ var somePermissionsTrue = false
+ // http://go/nl-kt-best-practices#for-loop-vs-foreach
+ for (it in permissionAndExpectedGrantResults) {
+ somePermissionsTrue = somePermissionsTrue || it.second
+ }
+ // When all permissions being requested are to be denied
+ // do not wait for windowTransition
+ if (!somePermissionsTrue) {
+ shouldWaitForWindowTransition = false
+ }
+ }
+ val result =
+ requestAppPermissions(
+ *permissions,
+ askTwice = askTwice,
+ waitForWindowTransition = shouldWaitForWindowTransition,
+ block = block
+ )
+ assertEquals(
+ "Permission request result had unexpected resultCode:",
+ Activity.RESULT_OK,
+ result.resultCode
+ )
+
+ val responseSize: Int =
+ result.resultData!!.getStringArrayExtra("$APP_PACKAGE_NAME.PERMISSIONS")!!.size
+ assertEquals(
+ "Permission request result had unexpected number of grant results:",
+ responseSize,
+ result.resultData!!.getIntArrayExtra("$APP_PACKAGE_NAME.GRANT_RESULTS")!!.size
+ )
+
+ // Note that the behavior around requesting `null` permissions changed in the platform
+ // in Android U. Currently, null permissions are ignored and left out of the result set.
+ assertTrue(
+ "Permission request result had fewer permissions than request",
+ permissions.size >= responseSize
+ )
+ assertEquals(
+ "Permission request result had unexpected grant results:",
+ permissionAndExpectedGrantResults.filter { it.first != null }.toList(),
+ result.resultData!!
+ .getStringArrayExtra("$APP_PACKAGE_NAME.PERMISSIONS")!!
+ .filterNotNull()
+ .zip(
+ result.resultData!!.getIntArrayExtra("$APP_PACKAGE_NAME.GRANT_RESULTS")!!.map {
+ it == PackageManager.PERMISSION_GRANTED
+ }
+ )
+ )
+
+ permissionAndExpectedGrantResults.forEach {
+ it.first?.let { permission -> assertAppHasPermission(permission, it.second) }
+ }
+ }
+
+ protected inline fun requestAppPermissionsAndAssertResult(
+ vararg permissionAndExpectedGrantResults: Pair<String?, Boolean>,
+ askTwice: Boolean = false,
+ waitForWindowTransition: Boolean = !isWatch,
+ crossinline block: () -> Unit
+ ) {
+ requestAppPermissionsAndAssertResult(
+ permissionAndExpectedGrantResults.map { it.first }.toTypedArray(),
+ permissionAndExpectedGrantResults,
+ askTwice,
+ waitForWindowTransition,
+ block
+ )
+ }
+
+ // Perform the requested action, then wait both for the action to complete, and for at least
+ // one window transition to occur since the moment the action begins executing.
+ protected inline fun doAndWaitForWindowTransition(crossinline block: () -> Unit) {
+ val timeoutOccurred =
+ !uiDevice.performActionAndWait(
+ { block() },
+ Until.newWindow(),
+ NEW_WINDOW_TIMEOUT_MILLIS
+ )
+
+ if (timeoutOccurred) {
+ throw RuntimeException("Timed out waiting for window transition.")
+ }
+ }
+
+ protected fun findPermissionRequestAllowButton(timeoutMillis: Long = 20000) {
+ if (isAutomotive || isWatch) {
+ waitFindObject(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)), timeoutMillis)
+ } else {
+ waitFindObject(By.res(ALLOW_BUTTON), timeoutMillis)
+ }
+ }
+
+ protected fun clickPermissionRequestAllowButton(timeoutMillis: Long = 20000) {
+ if (isAutomotive || isWatch) {
+ click(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)), timeoutMillis)
+ } else {
+ click(By.res(ALLOW_BUTTON), timeoutMillis)
+ }
+ }
+
+ protected fun clickPermissionRequestAllowAllButton(timeoutMillis: Long = 20000) {
+ click(By.res(ALLOW_ALL_BUTTON), timeoutMillis)
+ }
+
+ /**
+ * Only for use in tests that are not testing the notification permission popup, on T devices
+ */
+ protected fun clickNotificationPermissionRequestAllowButtonIfAvailable() {
+ if (SdkLevel.isAtLeastT() && getTargetSdk() < Build.VERSION_CODES.TIRAMISU) {
+ val notificationPermissionRequestVisible =
+ uiDevice.wait(
+ Until.hasObject(
+ By.text(getPermissionControllerString(NOTIF_TEXT, APP_PACKAGE_NAME))
+ ),
+ 1000
+ )
+ if (notificationPermissionRequestVisible) {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)))
+ } else {
+ click(By.res(ALLOW_BUTTON))
+ }
+ }
+ }
+ }
+
+ protected fun clickPermissionRequestSettingsLinkAndAllowAlways() {
+ clickPermissionRequestSettingsLink()
+ eventually({ clickAllowAlwaysInSettings() }, TIMEOUT_MILLIS * 2)
+ pressBack()
+ }
+
+ protected fun clickAllowAlwaysInSettings() {
+ if (isAutomotive || isTv || isWatch) {
+ click(By.text(getPermissionControllerString("app_permission_button_allow_always")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ }
+ }
+
+ protected fun clickAllowForegroundInSettings() {
+ click(By.res(ALLOW_FOREGROUND_RADIO_BUTTON))
+ }
+
+ protected fun clicksDenyInSettings() {
+ if (isAutomotive || isWatch) {
+ click(By.text(getPermissionControllerString("app_permission_button_deny")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/deny_radio_button"))
+ }
+ }
+
+ protected fun findPermissionRequestAllowForegroundButton(timeoutMillis: Long = 20000) {
+ if (isAutomotive || isWatch) {
+ waitFindObject(
+ By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)),
+ timeoutMillis
+ )
+ } else {
+ waitFindObject(By.res(ALLOW_FOREGROUND_BUTTON), timeoutMillis)
+ }
+ }
+
+ protected fun clickPermissionRequestAllowForegroundButton(timeoutMillis: Long = 20_000) {
+ if (isAutomotive || isWatch) {
+ click(
+ By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)),
+ timeoutMillis
+ )
+ } else {
+ click(By.res(ALLOW_FOREGROUND_BUTTON), timeoutMillis)
+ }
+ }
+
+ protected fun clickPermissionRequestDenyButton() {
+ if (isAutomotive) {
+ scrollToBottom();
+ clickAndWaitForWindowTransition(
+ By.text(getPermissionControllerString(DENY_BUTTON_TEXT))
+ )
+ } else if (isWatch || isTv) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ } else {
+ click(By.res(DENY_BUTTON))
+ }
+ }
+
+ protected fun clickPermissionRequestSettingsLinkAndDeny() {
+ clickPermissionRequestSettingsLink()
+ eventually({ clicksDenyInSettings() }, TIMEOUT_MILLIS * 2)
+ pressBack()
+ }
+
+ protected fun clickPermissionRequestSettingsLink() {
+ eventually {
+ if (isWatch) {
+ clickPermissionRequestSettingsLinkForWear()
+ return@eventually
+ }
+ // UiObject2 doesn't expose CharSequence.
+ val node =
+ if (isAutomotive) {
+ // Should match "Allow in settings." (location) and "go to settings." (body
+ // sensors)
+ uiAutomation.rootInActiveWindow
+ .findAccessibilityNodeInfosByText(" settings.")[0]
+ } else {
+ uiAutomation.rootInActiveWindow
+ .findAccessibilityNodeInfosByViewId(DETAIL_MESSAGE_ID)[0]
+ }
+ if (!node.isVisibleToUser) {
+ scrollToBottom()
+ }
+ assertTrue(node.isVisibleToUser)
+
+ val text = node.text as Spanned
+ val clickableSpan = text.getSpans(0, text.length, ClickableSpan::class.java)[0]
+ // We could pass in null here in Java, but we need an instance in Kotlin.
+ doAndWaitForWindowTransition { clickableSpan.onClick(View(context)) }
+ }
+ }
+
+ private fun clickPermissionRequestSettingsLinkForWear() {
+ // Find detail message.
+ val text = waitFindObject(By.textContains(" settings."))
+
+ // Move the view to the top of the screen.
+ var visibleBounds = text.getVisibleBounds()
+ val centerX = (visibleBounds.left + visibleBounds.right) / 2
+ uiDevice.drag(centerX, visibleBounds.top, centerX, 0, 10)
+
+ // Click the deep link.
+ // Not sure where the clickable text is. So try different point in the last line
+ // of the 5 line text.
+ val bounds = text.getVisibleBounds()
+ val xdelta = 0.2 * bounds.width()
+ val y = bounds.bottom - (0.05 * bounds.height())
+ var clickedOnLink: Boolean = false
+ for (i in 1..4) {
+ val x = bounds.left + (i * xdelta)
+ uiDevice.click(x.toInt(), y.toInt())
+ waitForIdleLong()
+ val nextScreenNode: AccessibilityNodeInfo? =
+ findAccessibilityNodeInfosByTextForSurfaceView(
+ uiAutomation.rootInActiveWindow,
+ "All the time")
+ if (nextScreenNode != null) {
+ clickedOnLink = true
+ break
+ }
+ }
+ assertTrue("Could not click on the settings link correctly", clickedOnLink)
+ }
+
+ protected fun clickPermissionRequestDenyAndDontAskAgainButton() {
+ if (isAutomotive) {
+ scrollToBottom();
+ clickAndWaitForWindowTransition(
+ By.text(getPermissionControllerString(DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT))
+ )
+ } else if (isWatch) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ } else {
+ click(By.res(DENY_AND_DONT_ASK_AGAIN_BUTTON))
+ }
+ }
+
+ // Only used in TV and Watch form factors
+ protected fun clickPermissionRequestDontAskAgainButton() {
+ if (isWatch) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ } else {
+ click(
+ By.res("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button")
+ )
+ }
+ }
+
+ protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() {
+ if (isAutomotive || isWatch) {
+ click(By.text(getPermissionControllerString(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT)))
+ } else {
+ click(By.res(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON))
+ }
+ }
+
+ protected fun clickPermissionRationaleContentInAppPermission() {
+ clickAndWaitForWindowTransition(By.res(APP_PERMISSION_RATIONALE_CONTENT_VIEW))
+ }
+
+ protected fun clickPermissionRationaleViewInGrantDialog() {
+ clickAndWaitForWindowTransition(By.res(GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW))
+ }
+
+ protected fun grantAppPermissionsByUi(vararg permissions: String) {
+ setAppPermissionState(*permissions, state = PermissionState.ALLOWED, isLegacyApp = false)
+ }
+
+ protected fun grantRuntimePermissions(vararg permissions: String) {
+ for (permission in permissions) {
+ uiAutomation.grantRuntimePermission(APP_PACKAGE_NAME, permission)
+ }
+ }
+
+ protected fun revokeAppPermissionsByUi(
+ vararg permissions: String,
+ isLegacyApp: Boolean = false
+ ) {
+ setAppPermissionState(
+ *permissions,
+ state = PermissionState.DENIED,
+ isLegacyApp = isLegacyApp
+ )
+ }
+
+ private fun navigateToAppPermissionSettings() {
+ if (isTv) {
+ clearTargetSdkWarning(1000L)
+ pressHome()
+ } else {
+ pressBack()
+ pressBack()
+ pressBack()
+ }
+
+ // Try multiple times as the AppInfo page might have read stale data
+ eventually(
+ {
+ try {
+ // Open the app details settings
+ doAndWaitForWindowTransition {
+ context.startActivity(
+ Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+ data = Uri.fromParts("package", APP_PACKAGE_NAME, null)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ )
+ }
+ if (isTv) {
+ pressDPadDown()
+ pressDPadDown()
+ pressDPadDown()
+ pressDPadDown()
+ }
+ // Open the permissions UI
+ clickAndWaitForWindowTransition(byTextRes(R.string.permissions).enabled(true))
+ } catch (e: Exception) {
+ pressBack()
+ throw e
+ }
+ },
+ TIMEOUT_MILLIS
+ )
+ }
+
+ private fun getTargetSdk(packageName: String = APP_PACKAGE_NAME): Int {
+ return callWithShellPermissionIdentity {
+ try {
+ context.packageManager.getApplicationInfo(packageName, 0).targetSdkVersion
+ } catch (e: PackageManager.NameNotFoundException) {
+ -1
+ }
+ }
+ }
+
+ protected fun navigateToIndividualPermissionSetting(
+ permission: String,
+ manuallyNavigate: Boolean = false
+ ) {
+ val useLegacyNavigation = isWatch || isAutomotive || manuallyNavigate
+ if (useLegacyNavigation) {
+ navigateToAppPermissionSettings()
+ val permissionLabel = getPermissionLabel(permission)
+ if (isWatch) {
+ clickAndWaitForWindowTransition(By.text(permissionLabel), 40_000)
+ } else {
+ clickPermissionControllerUi(By.text(permissionLabel))
+ }
+ return
+ }
+ doAndWaitForWindowTransition {
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSION).apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ putExtra(Intent.EXTRA_PERMISSION_NAME, permission)
+ putExtra(Intent.EXTRA_USER, Process.myUserHandle())
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ )
+ }
+ }
+ }
+
+ /** Starts activity with intent [ACTION_REVIEW_APP_DATA_SHARING_UPDATES]. */
+ fun startAppDataSharingUpdatesActivity() {
+ doAndWaitForWindowTransition {
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(ACTION_REVIEW_APP_DATA_SHARING_UPDATES).apply {
+ addFlags(FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
+ }
+ }
+ }
+
+ private fun setAppPermissionState(
+ vararg permissions: String,
+ state: PermissionState,
+ isLegacyApp: Boolean,
+ manuallyNavigate: Boolean = false,
+ ) {
+ val useLegacyNavigation = isWatch || isAutomotive || manuallyNavigate
+ if (useLegacyNavigation) {
+ navigateToAppPermissionSettings()
+ }
+
+ val navigatedGroupLabels = mutableSetOf<String>()
+ for (permission in permissions) {
+ // Find the permission screen
+ val permissionLabel = getPermissionLabel(permission)
+ if (navigatedGroupLabels.contains(getPermissionLabel(permission))) {
+ continue
+ }
+ navigatedGroupLabels.add(permissionLabel)
+ if (useLegacyNavigation) {
+ if (isWatch) {
+ click(By.text(permissionLabel), 40_000)
+ } else if (isAutomotive) {
+ clickPermissionControllerUi(permissionLabel)
+ } else {
+ clickPermissionControllerUi(By.text(permissionLabel))
+ }
+ } else {
+ doAndWaitForWindowTransition {
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSION).apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ putExtra(Intent.EXTRA_PERMISSION_NAME, permission)
+ putExtra(Intent.EXTRA_USER, Process.myUserHandle())
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ }
+ )
+ }
+ }
+ }
+
+ val wasGranted =
+ if (isAutomotive) {
+ // Automotive doesn't support one time permissions, and thus
+ // won't show an "Ask every time" message
+ !waitFindObject(
+ By.text(getPermissionControllerString("app_permission_button_deny"))
+ )
+ .isChecked
+ } else if (isTv || isWatch) {
+ !(waitFindObject(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ .isChecked ||
+ (!isLegacyApp &&
+ hasAskButton(permission) &&
+ waitFindObject(By.text(getPermissionControllerString(ASK_BUTTON_TEXT)))
+ .isChecked))
+ } else {
+ !(waitFindObject(By.res(DENY_RADIO_BUTTON)).isChecked ||
+ (!isLegacyApp &&
+ hasAskButton(permission) &&
+ waitFindObject(By.res(ASK_RADIO_BUTTON)).isChecked))
+ }
+ var alreadyChecked = false
+ val button =
+ waitFindObject(
+ if (isAutomotive) {
+ // Automotive doesn't support one time permissions, and thus
+ // won't show an "Ask every time" message
+ when (state) {
+ PermissionState.ALLOWED ->
+ if (showsForegroundOnlyButton(permission)) {
+ By.text(
+ getPermissionControllerString(
+ "app_permission_button_allow_foreground"
+ )
+ )
+ } else {
+ By.text(
+ getPermissionControllerString("app_permission_button_allow")
+ )
+ }
+ PermissionState.DENIED ->
+ By.text(getPermissionControllerString("app_permission_button_deny"))
+ PermissionState.DENIED_WITH_PREJUDICE ->
+ By.text(getPermissionControllerString("app_permission_button_deny"))
+ }
+ } else if (isTv || isWatch) {
+ when (state) {
+ PermissionState.ALLOWED ->
+ if (showsForegroundOnlyButton(permission)) {
+ By.text(
+ getPermissionControllerString(
+ ALLOW_FOREGROUND_PREFERENCE_TEXT
+ )
+ )
+ } else {
+ byAnyText(
+ getPermissionControllerResString(ALLOW_BUTTON_TEXT),
+ getPermissionControllerResString(
+ ALLOW_ALL_FILES_BUTTON_TEXT
+ )
+ )
+ }
+ PermissionState.DENIED ->
+ if (!isLegacyApp && hasAskButton(permission)) {
+ By.text(getPermissionControllerString(ASK_BUTTON_TEXT))
+ } else {
+ By.text(getPermissionControllerString(DENY_BUTTON_TEXT))
+ }
+ PermissionState.DENIED_WITH_PREJUDICE ->
+ By.text(getPermissionControllerString(DENY_BUTTON_TEXT))
+ }
+ } else {
+ when (state) {
+ PermissionState.ALLOWED ->
+ if (showsForegroundOnlyButton(permission)) {
+ By.res(ALLOW_FOREGROUND_RADIO_BUTTON)
+ } else if (showsAlwaysButton(permission)) {
+ By.res(ALLOW_ALWAYS_RADIO_BUTTON)
+ } else {
+ By.res(ALLOW_RADIO_BUTTON)
+ }
+ PermissionState.DENIED ->
+ if (!isLegacyApp && hasAskButton(permission)) {
+ By.res(ASK_RADIO_BUTTON)
+ } else {
+ By.res(DENY_RADIO_BUTTON)
+ }
+ PermissionState.DENIED_WITH_PREJUDICE -> By.res(DENY_RADIO_BUTTON)
+ }
+ }
+ )
+ alreadyChecked = button.isChecked
+ if (!alreadyChecked) {
+ button.click()
+ }
+
+ val shouldShowStorageWarning =
+ SdkLevel.isAtLeastT() &&
+ getTargetSdk() <= Build.VERSION_CODES.S_V2 &&
+ permission in MEDIA_PERMISSIONS
+ if (shouldShowStorageWarning) {
+ if (isWatch) {
+ click(
+ By.desc(
+ getPermissionControllerString("media_confirm_dialog_positive_button")
+ )
+ )
+ } else {
+ click(By.res(ALERT_DIALOG_OK_BUTTON))
+ }
+ } else if (!alreadyChecked && isLegacyApp && wasGranted) {
+ if (!isTv) {
+ // Wait for alert dialog to popup, then scroll to the bottom of it
+ if (isWatch) {
+ waitFindObject(
+ By.text(getPermissionControllerString("old_sdk_deny_warning"))
+ )
+ } else {
+ waitFindObject(By.res(ALERT_DIALOG_MESSAGE))
+ }
+ scrollToBottom()
+ }
+
+ // Due to the limited real estate, Wear uses buttons with icons instead of text
+ // for dialogs
+ if (isWatch) {
+ click(By.desc(getPermissionControllerString("ok")))
+ } else {
+ val resources =
+ context
+ .createPackageContext(packageManager.permissionControllerPackageName, 0)
+ .resources
+ val confirmTextRes =
+ resources.getIdentifier(
+ "com.android.permissioncontroller:string/grant_dialog_button_deny_anyway",
+ null,
+ null
+ )
+
+ val confirmText = resources.getString(confirmTextRes)
+ click(byTextStartsWithCaseInsensitive(confirmText))
+ }
+ }
+ pressBack()
+ }
+ pressBack()
+ pressBack()
+ }
+
+ private fun getPermissionLabel(permission: String): String {
+ val labelResName = permissionToLabelResNameMap[permission]
+ assertNotNull("Unknown permission $permission", labelResName)
+ val labelRes = platformResources.getIdentifier(labelResName, null, null)
+ return platformResources.getString(labelRes)
+ }
+
+ private fun hasAskButton(permission: String): Boolean =
+ when (permission) {
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.RECORD_AUDIO,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION -> true
+ else -> false
+ }
+ private fun showsAllowPhotosButton(permission: String): Boolean {
+ if (!isPhotoPickerPermissionPromptEnabled()) {
+ return false
+ }
+ return when (permission) {
+ Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO -> true
+ else -> false
+ }
+ }
+
+ private fun showsForegroundOnlyButton(permission: String): Boolean =
+ when (permission) {
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.RECORD_AUDIO -> true
+ else -> false
+ }
+
+ private fun showsAlwaysButton(permission: String): Boolean =
+ when (permission) {
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION -> true
+ else -> false
+ }
+
+ private fun scrollToBottom() {
+ val scrollable =
+ UiScrollable(UiSelector().scrollable(true)).apply {
+ if (isWatch) {
+ swipeDeadZonePercentage = 0.1
+ } else {
+ swipeDeadZonePercentage = 0.25
+ }
+ }
+ waitForIdle()
+ if (scrollable.exists()) {
+ try {
+ scrollable.flingToEnd(10)
+ } catch (e: UiObjectNotFoundException) {
+ // flingToEnd() sometimes still fails despite waitForIdle() and the exists() check
+ // (b/246984354).
+ e.printStackTrace()
+ }
+ }
+ }
+
+ protected fun findAccessibilityNodeInfosByTextForSurfaceView(
+ node: AccessibilityNodeInfo,
+ text: String
+ ): AccessibilityNodeInfo? {
+ if (node.text != null && node.text.contains(text)) return node
+ for (i in 0 until node.childCount) {
+ val child = node.getChild(i)
+ if (child != null) {
+ return findAccessibilityNodeInfosByTextForSurfaceView(child, text) ?: continue
+ }
+ }
+ return null
+ }
+
+ private fun byTextRes(textRes: Int): BySelector = By.text(context.getString(textRes))
+
+ private fun byTextStartsWithCaseInsensitive(prefix: String): BySelector =
+ By.text(Pattern.compile("(?i)^${Pattern.quote(prefix)}.*$"))
+
+ protected fun assertAppHasPermission(permissionName: String, expectPermission: Boolean) {
+ val checkPermissionResult = packageManager.checkPermission(permissionName, APP_PACKAGE_NAME)
+ assertTrue(
+ "Invalid permission check result: $checkPermissionResult",
+ checkPermissionResult == PackageManager.PERMISSION_GRANTED ||
+ checkPermissionResult == PackageManager.PERMISSION_DENIED
+ )
+ if (!expectPermission && checkPermissionResult == PackageManager.PERMISSION_GRANTED) {
+ Assert.fail(
+ "Unexpected permission check result for $permissionName: " +
+ "expected -1 (PERMISSION_DENIED) but was 0 (PERMISSION_GRANTED)"
+ )
+ }
+ if (expectPermission && checkPermissionResult == PackageManager.PERMISSION_DENIED) {
+ Assert.fail(
+ "Unexpected permission check result for $permissionName: " +
+ "expected 0 (PERMISSION_GRANTED) but was -1 (PERMISSION_DENIED)"
+ )
+ }
+ }
+
+ protected fun assertAppHasCalendarAccess(expectAccess: Boolean) {
+ val future =
+ startActivityForFuture(
+ Intent().apply {
+ component =
+ ComponentName(
+ APP_PACKAGE_NAME,
+ "$APP_PACKAGE_NAME.CheckCalendarAccessActivity"
+ )
+ }
+ )
+ clickNotificationPermissionRequestAllowButtonIfAvailable()
+ val result = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ assertEquals(Activity.RESULT_OK, result.resultCode)
+ assertTrue(result.resultData!!.hasExtra("$APP_PACKAGE_NAME.HAS_ACCESS"))
+ assertEquals(
+ expectAccess,
+ result.resultData!!.getBooleanExtra("$APP_PACKAGE_NAME.HAS_ACCESS", false)
+ )
+ }
+
+ protected fun assertPermissionFlags(permName: String, vararg flags: Pair<Int, Boolean>) {
+ val user = Process.myUserHandle()
+ SystemUtil.runWithShellPermissionIdentity {
+ val currFlags = packageManager.getPermissionFlags(permName, APP_PACKAGE_NAME, user)
+ for ((flag, set) in flags) {
+ assertEquals("flag $flag: ", set, currFlags and flag != 0)
+ }
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
new file mode 100644
index 000000000..7bcd8a911
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/CameraMicIndicatorsPermissionTest.kt
@@ -0,0 +1,757 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.app.compat.CompatChanges
+import android.content.AttributionSource
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.hardware.camera2.CameraManager
+import android.os.Build
+import android.os.Process
+import android.os.SystemClock
+import android.os.SystemProperties
+import android.permission.PermissionManager
+import android.permission.cts.MtsIgnore
+import android.platform.test.annotations.AsbSecurityTest
+import android.platform.test.rule.ScreenRecordRule
+import android.provider.DeviceConfig
+import android.provider.Settings
+import android.safetycenter.SafetyCenterManager
+import android.server.wm.WindowManagerStateHelper
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.StaleObjectException
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.UiSelector
+import com.android.compatibility.common.util.CddTest
+import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.UiAutomatorUtils2
+import com.android.compatibility.common.util.UiAutomatorUtils2.assertWithUiDump
+import com.android.modules.utils.build.SdkLevel
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase
+import java.util.regex.Pattern
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+private const val APK_PATH =
+ "/data/local/tmp/cts-permissionui/CtsAppThatAccessesMicAndCameraPermission.apk"
+private const val APP_LABEL = "CtsCameraMicAccess"
+private const val APP_PKG = "android.permissionui.cts.appthataccessescameraandmic"
+private const val SHELL_PKG = "com.android.shell"
+private const val USE_CAMERA = "use_camera"
+private const val USE_MICROPHONE = "use_microphone"
+private const val USE_HOTWORD = "use_hotword"
+private const val FINISH_EARLY = "finish_early"
+private const val USE_INTENT_ACTION = "test.action.USE_CAMERA_OR_MIC"
+private const val PRIVACY_CHIP_ID = "com.android.systemui:id/privacy_chip"
+private const val PRIVACY_ITEM_ID = "com.android.systemui:id/privacy_item"
+private const val INDICATORS_FLAG = "camera_mic_icons_enabled"
+private const val WEAR_MIC_LABEL = "Microphone"
+private const val PERMISSION_INDICATORS_NOT_PRESENT = 162547999L
+private const val IDLE_TIMEOUT_MILLIS: Long = 1000
+private const val UNEXPECTED_TIMEOUT_MILLIS = 1000L
+private const val TIMEOUT_MILLIS: Long = 20000
+private const val TV_MIC_INDICATOR_WINDOW_TITLE = "MicrophoneCaptureIndicator"
+private const val MIC_LABEL_NAME = "microphone_toggle_label_qs"
+private const val CAMERA_LABEL_NAME = "camera_toggle_label_qs"
+private val HOTWORD_DETECTION_SERVICE_REQUIRED =
+ SystemProperties.getBoolean("ro.hotword.detection_service_required", false)
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+@ScreenRecordRule.ScreenRecord
+@FlakyTest
+class CameraMicIndicatorsPermissionTest : StsExtraBusinessLogicTestCase {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.context
+ private val uiAutomation: UiAutomation = instrumentation.uiAutomation
+ private val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+ private val packageManager: PackageManager = context.packageManager
+ private val permissionManager: PermissionManager =
+ context.getSystemService(PermissionManager::class.java)!!
+
+ private val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ private val isCar = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ private val isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ private val originalCameraLabel =
+ packageManager
+ .getPermissionGroupInfo(Manifest.permission_group.CAMERA, 0)
+ .loadLabel(packageManager)
+ .toString()
+ private val originalMicLabel =
+ packageManager
+ .getPermissionGroupInfo(Manifest.permission_group.MICROPHONE, 0)
+ .loadLabel(packageManager)
+ .toString()
+ private val cameraLabel = originalCameraLabel.lowercase()
+ private val micLabel = originalMicLabel.lowercase()
+ private var wasEnabled = false
+ private var isScreenOn = false
+ private var screenTimeoutBeforeTest: Long = 0L
+ private lateinit var carMicPrivacyChipId: String
+ private lateinit var carCameraPrivacyChipId: String
+
+ @get:Rule val disableAnimationRule = DisableAnimationRule()
+
+ @get:Rule val screenRecordRule = ScreenRecordRule(false, false)
+
+ constructor() : super()
+
+ companion object {
+ private const val AUTO_MIC_INDICATOR_DISMISSAL_TIMEOUT_MS = 30_000L
+ const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+ const val DELAY_MILLIS = 3000L
+ private val TAG = CameraMicIndicatorsPermissionTest::class.java.simpleName
+ }
+
+ private val safetyCenterEnabled = callWithShellPermissionIdentity {
+ DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_CENTER_ENABLED,
+ false.toString()
+ )!!
+ }
+
+ private fun uninstall() {
+ val output = runShellCommand("pm uninstall $APP_PKG").trim()
+ assertEquals("Success", output)
+ }
+
+ private fun install() {
+ val output = runShellCommandOrThrow("pm install -g $APK_PATH").trim()
+ assertEquals("Success", output)
+ }
+
+ @Before
+ fun setUp() {
+ runWithShellPermissionIdentity {
+ screenTimeoutBeforeTest =
+ Settings.System.getLong(context.contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
+ Settings.System.putLong(
+ context.contentResolver,
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ 1800000L
+ )
+ }
+
+ if (!isScreenOn) {
+ uiDevice.wakeUp()
+ runShellCommand(instrumentation, "wm dismiss-keyguard")
+ Thread.sleep(DELAY_MILLIS)
+ isScreenOn = true
+ }
+ uiDevice.findObject(By.text("Close"))?.click()
+ wasEnabled = setIndicatorsEnabledStateIfNeeded(true)
+ // If the change Id is not present, then isChangeEnabled will return true. To bypass this,
+ // the change is set to "false" if present.
+ assumeFalse(
+ "feature not present on this device",
+ callWithShellPermissionIdentity {
+ CompatChanges.isChangeEnabled(PERMISSION_INDICATORS_NOT_PRESENT, Process.SYSTEM_UID)
+ }
+ )
+ install()
+ }
+
+ private fun setIndicatorsEnabledStateIfNeeded(shouldBeEnabled: Boolean): Boolean {
+ var currentlyEnabled = false
+ runWithShellPermissionIdentity {
+ currentlyEnabled =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, INDICATORS_FLAG, true)
+ if (currentlyEnabled != shouldBeEnabled) {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ INDICATORS_FLAG,
+ shouldBeEnabled.toString(),
+ false
+ )
+ }
+ }
+ return currentlyEnabled
+ }
+
+ @After
+ fun tearDown() {
+ uninstall()
+ if (isCar) {
+ // Deselect the indicator since it persists otherwise
+ pressBack()
+ }
+ eventually(
+ { assertIndicatorsShown(false, false, false) },
+ AUTO_MIC_INDICATOR_DISMISSAL_TIMEOUT_MS
+ )
+ if (!wasEnabled) {
+ setIndicatorsEnabledStateIfNeeded(false)
+ }
+ runWithShellPermissionIdentity {
+ Settings.System.putLong(
+ context.contentResolver,
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ screenTimeoutBeforeTest
+ )
+ }
+ changeSafetyCenterFlag(safetyCenterEnabled)
+ if (!isTv) {
+ pressBack()
+ pressBack()
+ }
+ pressHome()
+ pressHome()
+ }
+
+ private fun openApp(
+ useMic: Boolean,
+ useCamera: Boolean,
+ useHotword: Boolean,
+ finishEarly: Boolean = false
+ ) {
+ context.startActivity(
+ Intent(USE_INTENT_ACTION).apply {
+ putExtra(USE_CAMERA, useCamera)
+ putExtra(USE_MICROPHONE, useMic)
+ putExtra(USE_HOTWORD, useHotword)
+ putExtra(FINISH_EARLY, finishEarly)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
+ }
+
+ @Test
+ @CddTest(requirement = "9.8.2/H-5-1,T-5-1,A-2-1")
+ fun testCameraIndicator() {
+ // If camera is not available skip the test
+ assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
+ val manager = context.getSystemService(CameraManager::class.java)!!
+ assumeTrue(manager.cameraIdList.isNotEmpty())
+ changeSafetyCenterFlag(false.toString())
+ testCameraAndMicIndicator(useMic = false, useCamera = true)
+ }
+
+ @Test
+ @CddTest(requirement = "9.8.2/H-4-1,T-4-1,A-1-1")
+ fun testMicIndicator() {
+ changeSafetyCenterFlag(false.toString())
+ testCameraAndMicIndicator(useMic = true, useCamera = false)
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = [258672042])
+ @MtsIgnore(bugId = 351903707)
+ fun testMicIndicatorWithManualFinishOpStillShows() {
+ changeSafetyCenterFlag(false.toString())
+ testCameraAndMicIndicator(useMic = true, useCamera = false, finishEarly = true)
+ }
+
+ @Test
+ @CddTest(requirement = "9.8.2/H-4-1,T-4-1,A-1-1")
+ fun testHotwordIndicatorBehavior() {
+ changeSafetyCenterFlag(false.toString())
+ testCameraAndMicIndicator(useMic = false, useCamera = false, useHotword = true)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ fun testChainUsageWithOtherUsage() {
+ // TV has only the mic icon
+ assumeFalse(isTv)
+ // Car has separate panels for mic and camera for now.
+ // TODO(b/218788634): enable this test for car once the new camera indicator is implemented.
+ assumeFalse(isCar)
+ // If camera is not available skip the test
+ assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
+ changeSafetyCenterFlag(false.toString())
+ testCameraAndMicIndicator(useMic = false, useCamera = true, chainUsage = true)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ fun testSafetyCenterCameraIndicator() {
+ assumeFalse(isTv)
+ assumeFalse(isCar)
+ val manager = context.getSystemService(CameraManager::class.java)!!
+ assumeTrue(manager.cameraIdList.isNotEmpty())
+ changeSafetyCenterFlag(true.toString())
+ assumeSafetyCenterEnabled()
+ testCameraAndMicIndicator(useMic = false, useCamera = true, safetyCenterEnabled = true)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ fun testSafetyCenterMicIndicator() {
+ assumeFalse(isTv)
+ assumeFalse(isCar)
+ changeSafetyCenterFlag(true.toString())
+ assumeSafetyCenterEnabled()
+ testCameraAndMicIndicator(useMic = true, useCamera = false, safetyCenterEnabled = true)
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ fun testSafetyCenterHotwordIndicatorBehavior() {
+ assumeFalse(isTv)
+ assumeFalse(isCar)
+ assumeTrue(HOTWORD_DETECTION_SERVICE_REQUIRED)
+ changeSafetyCenterFlag(true.toString())
+ assumeSafetyCenterEnabled()
+ testCameraAndMicIndicator(
+ useMic = false,
+ useCamera = false,
+ useHotword = true,
+ safetyCenterEnabled = true
+ )
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ fun testSafetyCenterChainUsageWithOtherUsage() {
+ assumeFalse(isTv)
+ assumeFalse(isCar)
+ changeSafetyCenterFlag(true.toString())
+ assumeSafetyCenterEnabled()
+ testCameraAndMicIndicator(
+ useMic = false,
+ useCamera = true,
+ chainUsage = true,
+ safetyCenterEnabled = true
+ )
+ }
+
+ private fun testCameraAndMicIndicator(
+ useMic: Boolean,
+ useCamera: Boolean,
+ useHotword: Boolean = false,
+ chainUsage: Boolean = false,
+ safetyCenterEnabled: Boolean = false,
+ finishEarly: Boolean = false
+ ) {
+ Log.d(
+ TAG,
+ "testCameraAndMicIndicator useMic=$useMic useCamera=$useCamera " +
+ "safetyCenterEnabled=$safetyCenterEnabled finishEarly=$finishEarly"
+ )
+ // If camera is not available skip the test
+ if (useCamera) {
+ assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY))
+ }
+ var chainAttribution: AttributionSource? = null
+ openApp(useMic, useCamera, useHotword, finishEarly)
+ try {
+ eventually {
+ val appView =
+ if (isWatch) {
+ // Title is disabled by default on watch apps
+ uiDevice.findObject(UiSelector().packageName(APP_PKG))
+ } else {
+ uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ }
+
+ assertWithUiDump {
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ }
+ }
+ if (chainUsage) {
+ chainAttribution = createChainAttribution()
+ runWithShellPermissionIdentity {
+ val ret =
+ permissionManager.checkPermissionForStartDataDelivery(
+ Manifest.permission.RECORD_AUDIO,
+ chainAttribution!!,
+ ""
+ )
+ assertEquals(PermissionManager.PERMISSION_GRANTED, ret)
+ }
+ }
+
+ Log.d(TAG, "assert to make sure Indicators are displayed")
+ assertIndicatorsShown(useMic, useCamera, useHotword, chainUsage, safetyCenterEnabled)
+
+ if (finishEarly) {
+ // Assert that the indicator doesn't go away
+ var failed = false
+ try {
+ // Check if the indicator has gone away. This will throw an exception if the
+ // indicator is still present
+ Log.d(TAG, "checking on indicators again")
+ assertIndicatorsShown(false, false, false)
+ Log.d(TAG, "indicators are gone")
+ // If we successfully asserted that the indicator went away, fail the test
+ failed = true
+ } catch (t: Throwable) {
+ Log.d(TAG, "indicators are continuing to show")
+ // expected
+ }
+ if (failed) {
+ assertWithUiDump { Assert.fail("Expected the indicator to remain present") }
+ }
+ }
+ } finally {
+ if (chainAttribution != null) {
+ runWithShellPermissionIdentity {
+ permissionManager.finishDataDelivery(
+ Manifest.permission.RECORD_AUDIO,
+ chainAttribution
+ )
+ }
+ }
+ }
+ }
+
+ private fun assertIndicatorsShown(
+ useMic: Boolean,
+ useCamera: Boolean,
+ useHotword: Boolean = false,
+ chainUsage: Boolean = false,
+ safetyCenterEnabled: Boolean = false,
+ ) {
+ if (isTv) {
+ assertTvIndicatorsShown(useMic, useCamera, useHotword)
+ } else if (isCar) {
+ assertCarIndicatorsShown(useMic, useCamera, useHotword, chainUsage)
+ } else if (isWatch) {
+ assertWatchIndicatorsShown(useMic, useCamera, useHotword)
+ } else {
+ uiDevice.openQuickSettings()
+ val micInUse =
+ if (SdkLevel.isAtLeastU() && HOTWORD_DETECTION_SERVICE_REQUIRED) {
+ useMic || useHotword
+ } else {
+ useMic
+ }
+ assertPrivacyChipAndIndicatorsPresent(
+ micInUse,
+ useCamera,
+ chainUsage,
+ safetyCenterEnabled
+ )
+ uiDevice.pressBack()
+ }
+ }
+
+ private fun assertWatchIndicatorsShown(
+ useMic: Boolean,
+ useCamera: Boolean,
+ useHotword: Boolean
+ ) {
+ if (useMic || useHotword || (!useMic && !useCamera && !useHotword)) {
+ val iconView = UiAutomatorUtils2.waitFindObjectOrNull(By.descContains(WEAR_MIC_LABEL))
+ if (useMic) {
+ assertNotNull("Did not find mic chip", iconView)
+ } else {
+ assertNull("Found mic chip, but did not expect to", iconView)
+ // waitFindObject leaves the watch on the notification screen
+ pressBack()
+ }
+ }
+ }
+
+ private fun assertTvIndicatorsShown(useMic: Boolean, useCamera: Boolean, useHotword: Boolean) {
+ if (useMic || useHotword || (!useMic && !useCamera && !useHotword)) {
+ eventually {
+ val found =
+ WindowManagerStateHelper().waitFor(
+ "Waiting for the mic indicator window to come up"
+ ) {
+ it.containsWindow(TV_MIC_INDICATOR_WINDOW_TITLE) &&
+ it.isWindowVisible(TV_MIC_INDICATOR_WINDOW_TITLE)
+ }
+ if (useMic) {
+ assertTrue("Did not find chip", found)
+ } else {
+ assertFalse("Found chip, but did not expect to", found)
+ }
+ }
+ }
+ if (useCamera) {
+ // There is no camera indicator on TVs.
+ }
+ }
+
+ private fun assertCarIndicatorsShown(
+ useMic: Boolean,
+ useCamera: Boolean,
+ useHotword: Boolean,
+ chainUsage: Boolean
+ ) {
+ eventually {
+ // Ensure the privacy chip is present (or not)
+ carMicPrivacyChipId = context.getString(R.string.car_mic_privacy_chip_id)
+ carCameraPrivacyChipId = context.getString(R.string.car_camera_privacy_chip_id)
+ var micPrivacyChip = uiDevice.findObject(By.res(carMicPrivacyChipId))
+ var cameraPrivacyChip = uiDevice.findObject(By.res(carCameraPrivacyChipId))
+ if (useMic) {
+ assertNotNull("Did not find mic chip", micPrivacyChip)
+ // Click to chip to show the panel.
+ micPrivacyChip.click()
+ } else if (useCamera) {
+ assertNotNull("Did not find camera chip", cameraPrivacyChip)
+ // Click to chip to show the panel.
+ cameraPrivacyChip.click()
+ } else {
+ assertNull("Found mic chip, but did not expect to", micPrivacyChip)
+ assertNull("Found camera chip, but did not expect to", cameraPrivacyChip)
+ }
+ }
+
+ eventually {
+ if (chainUsage) {
+ // Not applicable for car
+ assertChainMicAndOtherCameraUsed(false)
+ return@eventually
+ }
+ if (useMic) {
+ // There should be a mic privacy panel after mic privacy chip is clicked
+ val micLabelView = uiDevice.findObject(UiSelector().textContains(micLabel))
+ assertTrue("View with text $micLabel not found", micLabelView.exists())
+ val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ } else if (useCamera) {
+ // There should be a camera privacy panel after camera privacy chip is clicked
+ val cameraLabelView = uiDevice.findObject(UiSelector().textContains(cameraLabel))
+ assertTrue("View with text $cameraLabel not found", cameraLabelView.exists())
+ val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ assertTrue("View with text $APP_LABEL not found", appView.exists())
+ } else {
+ // There should be no privacy panel when using hot word
+ val micLabelView = uiDevice.findObject(UiSelector().textContains(micLabel))
+ assertFalse(
+ "View with text $micLabel found, but did not expect to",
+ micLabelView.exists()
+ )
+ val cameraLabelView = uiDevice.findObject(UiSelector().textContains(cameraLabel))
+ assertFalse(
+ "View with text $cameraLabel found, but did not expect to",
+ cameraLabelView.exists()
+ )
+ val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL))
+ assertFalse(
+ "View with text $APP_LABEL found, but did not expect to",
+ appView.exists()
+ )
+ }
+ }
+ }
+
+ private fun assertPrivacyChipAndIndicatorsPresent(
+ useMic: Boolean,
+ useCamera: Boolean,
+ chainUsage: Boolean,
+ safetyCenterEnabled: Boolean = false
+ ) {
+ // Ensure the privacy chip is present
+ if (useCamera || useMic) {
+ eventually {
+ val privacyChip = UiAutomatorUtils2.waitFindObjectOrNull(By.res(PRIVACY_CHIP_ID))
+ assertWithUiDump {
+ assertNotNull("view with id $PRIVACY_CHIP_ID not found", privacyChip)
+ }
+ privacyChip.click()
+ }
+ } else {
+ Log.d(TAG, "waiting for PRIVACY_CHIP_ID to disappear")
+ assertWithUiDump { UiAutomatorUtils2.waitUntilObjectGone(By.res(PRIVACY_CHIP_ID)) }
+ return
+ }
+
+ eventually {
+ if (chainUsage) {
+ assertChainMicAndOtherCameraUsed(safetyCenterEnabled)
+ return@eventually
+ }
+ if (useMic) {
+ if (safetyCenterEnabled) {
+ assertSafetyCenterMicViewNotNull()
+ } else {
+ val iconView = waitFindObject(By.descContains(micLabel))
+ assertWithUiDump {
+ assertNotNull("View with description '$micLabel' not found", iconView)
+ }
+ }
+ }
+ if (useCamera) {
+ if (safetyCenterEnabled) {
+ assertSafetyCenterCameraViewNotNull()
+ } else {
+ val iconView = waitFindObject(By.descContains(cameraLabel))
+ assertWithUiDump {
+ assertNotNull("View with description '$cameraLabel' not found", iconView)
+ }
+ }
+ }
+ var appView = waitFindObject(By.textContains(APP_LABEL))
+ assertWithUiDump { assertNotNull("View with text $APP_LABEL not found", appView) }
+ }
+ uiDevice.pressBack()
+ }
+
+ private fun createChainAttribution(): AttributionSource? {
+ var attrSource: AttributionSource? = null
+ runWithShellPermissionIdentity {
+ try {
+ val appUid = packageManager.getPackageUid(APP_PKG, 0)
+ val childAttribution = AttributionSource(appUid, APP_PKG, null)
+ val attribution =
+ AttributionSource(
+ Process.myUid(),
+ context.packageName,
+ null,
+ null,
+ permissionManager.registerAttributionSource(childAttribution)
+ )
+ attrSource = permissionManager.registerAttributionSource(attribution)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Assert.fail("Expected to find a UID for $APP_LABEL")
+ }
+ }
+ return attrSource
+ }
+
+ private fun assertChainMicAndOtherCameraUsed(safetyCenterEnabled: Boolean) {
+ val shellLabel =
+ try {
+ context.packageManager
+ .getApplicationInfo(SHELL_PKG, 0)
+ .loadLabel(context.packageManager)
+ .toString()
+ } catch (e: PackageManager.NameNotFoundException) {
+ "Did not find shell package"
+ }
+
+ if (safetyCenterEnabled) {
+ assertSafetyCenterMicViewNotNull()
+ assertSafetyCenterCameraViewNotNull()
+ var shellView = waitFindObject(By.textContains(shellLabel))
+ assertNotNull("View with text $shellLabel not found", shellView)
+ } else {
+ val usageViews = uiDevice.findObjects(By.res(PRIVACY_ITEM_ID))
+ assertEquals("Expected two usage views", 2, usageViews.size)
+ val appViews = uiDevice.findObjects(By.textContains(APP_LABEL))
+ assertEquals("Expected two $APP_LABEL view", 2, appViews.size)
+ val shellView = uiDevice.findObjects(By.textContains(shellLabel))
+ assertEquals("Expected only one shell view", 1, shellView.size)
+ }
+ }
+
+ private fun pressBack() {
+ uiDevice.pressBack()
+ }
+
+ private fun pressHome() {
+ uiDevice.pressHome()
+ }
+
+ private fun changeSafetyCenterFlag(safetyCenterEnabled: String) {
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_CENTER_ENABLED,
+ safetyCenterEnabled,
+ false
+ )
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private fun assumeSafetyCenterEnabled() {
+ val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)!!
+ val isSafetyCenterEnabled: Boolean =
+ runWithShellPermissionIdentity<Boolean> { safetyCenterManager.isSafetyCenterEnabled }
+ assumeTrue(isSafetyCenterEnabled)
+ }
+
+ protected fun waitFindObject(selector: BySelector): UiObject2? {
+ return findObjectWithRetry({ t -> UiAutomatorUtils2.waitFindObject(selector, t) })
+ }
+
+ private fun findObjectWithRetry(
+ automatorMethod: (timeoutMillis: Long) -> UiObject2?,
+ timeoutMillis: Long = TIMEOUT_MILLIS
+ ): UiObject2? {
+ val startTime = SystemClock.elapsedRealtime()
+ return try {
+ automatorMethod(timeoutMillis)
+ } catch (e: StaleObjectException) {
+ val remainingTime = timeoutMillis - (SystemClock.elapsedRealtime() - startTime)
+ if (remainingTime <= 0) {
+ throw e
+ }
+ automatorMethod(remainingTime)
+ }
+ }
+
+ private fun getPermissionControllerString(resourceName: String): String {
+ val permissionControllerPkg = context.packageManager.permissionControllerPackageName
+ try {
+ val permissionControllerContext =
+ context.createPackageContext(permissionControllerPkg, 0)
+ val resourceId =
+ permissionControllerContext.resources.getIdentifier(
+ resourceName,
+ "string",
+ "com.android.permissioncontroller"
+ )
+ return permissionControllerContext.getString(resourceId)
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw RuntimeException(e)
+ }
+ }
+
+ private fun assertSafetyCenterMicViewNotNull() {
+ val safetyCenterMicLabel = getPermissionControllerString(MIC_LABEL_NAME)
+ val micView = waitFindObject(byOneOfText(originalMicLabel, safetyCenterMicLabel))
+ assertNotNull(
+ "View with text '$originalMicLabel' or '$safetyCenterMicLabel' not found",
+ micView
+ )
+ }
+
+ private fun assertSafetyCenterCameraViewNotNull() {
+ val safetyCenterCameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME)
+ val cameraView = waitFindObject(byOneOfText(originalCameraLabel, safetyCenterCameraLabel))
+ assertNotNull(
+ "View with text '$originalCameraLabel' or '$safetyCenterCameraLabel' not found",
+ cameraView
+ )
+ }
+
+ private fun byOneOfText(vararg textValues: String) =
+ By.text(Pattern.compile(textValues.joinToString(separator = "|") { Pattern.quote(it) }))
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt
new file mode 100644
index 000000000..9a09a4559
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest
+import android.app.AppOpsManager
+import android.app.Instrumentation
+import android.app.ecm.EnhancedConfirmationManager
+import android.content.Context
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Process
+import android.permission.flags.Flags
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+@AppModeFull(reason = "Instant apps cannot install packages")
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+@RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+@CddTest(requirement = "9.18/C-0-1")
+class EnhancedConfirmationManagerTest : BaseUsePermissionTest() {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.targetContext
+ private val ecm by lazy { context.getSystemService(EnhancedConfirmationManager::class.java)!! }
+ private val appOpsManager by lazy { context.getSystemService(AppOpsManager::class.java)!! }
+ private val packageManager by lazy { context.packageManager }
+
+ @Rule
+ @JvmField
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Before
+ fun assumeNotAutoTvOrWear() {
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ Assume.assumeFalse(
+ packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ )
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH))
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun installedAppStartsWithModeDefault() {
+ installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ assertEquals(
+ getAppEcmState(context, appOpsManager, APP_PACKAGE_NAME),
+ AppOpsManager.MODE_DEFAULT
+ )
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenStoreAppThenIsNotRestrictedFromProtectedSetting() {
+ installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenLocalAppThenIsRestrictedFromProtectedSetting() {
+ installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenDownloadedThenAppIsRestrictedFromProtectedSetting() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenExplicitlyRestrictedAppThenIsRestrictedFromProtectedSetting() {
+ installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ setAppEcmState(context, appOpsManager, APP_PACKAGE_NAME, AppOpsManager.MODE_ERRORED)
+ eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenRestrictedAppThenIsNotRestrictedFromNonProtectedSetting() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, NON_PROTECTED_SETTING)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenRestrictedAppThenClearRestrictionNotAllowedByDefault() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertFalse(ecm.isClearRestrictionAllowed(APP_PACKAGE_NAME)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun givenRestrictedAppWhenClearRestrictionThenNotRestrictedFromProtectedSetting() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST)
+ runWithShellPermissionIdentity {
+ eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ ecm.setClearRestrictionAllowed(APP_PACKAGE_NAME)
+ eventually { assertTrue(ecm.isClearRestrictionAllowed(APP_PACKAGE_NAME)) }
+ ecm.clearRestriction(APP_PACKAGE_NAME)
+ eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) }
+ }
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun createRestrictedSettingDialogIntentReturnsIntent() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST)
+
+ val intent = ecm.createRestrictedSettingDialogIntent(APP_PACKAGE_NAME, PROTECTED_SETTING)
+
+ assertNotNull(intent)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun grantDialogBlocksRestrictedPermissionsOfSameGroupTogether() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+ val permissionAndExpectedGrantResults =
+ arrayOf(
+ GROUP_1_PERMISSION_1_RESTRICTED to false,
+ GROUP_1_PERMISSION_2_RESTRICTED to false
+ )
+
+ requestAppPermissionsAndAssertResult(*permissionAndExpectedGrantResults) {
+ click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS)
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+
+ requestAppPermissionsAndAssertResult(
+ *permissionAndExpectedGrantResults,
+ waitForWindowTransition = false
+ ) {
+ assertNoEcmDialogShown()
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun grantDialogBlocksRestrictedPermissionsOfDifferentGroupsIndividually() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+ val permissionAndExpectedGrantResults =
+ arrayOf(
+ GROUP_1_PERMISSION_1_RESTRICTED to false,
+ GROUP_2_PERMISSION_1_RESTRICTED to false
+ )
+
+ requestAppPermissionsAndAssertResult(
+ *permissionAndExpectedGrantResults,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+
+ requestAppPermissionsAndAssertResult(
+ *permissionAndExpectedGrantResults,
+ waitForWindowTransition = false
+ ) {
+ assertNoEcmDialogShown()
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun grantDialogBlocksRestrictedGroupsThenRequestsUnrestrictedGroupsDespiteOutOfOrderRequest() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+
+ requestAppPermissionsAndAssertResult(
+ GROUP_3_PERMISSION_1_UNRESTRICTED to false,
+ GROUP_2_PERMISSION_1_RESTRICTED to false,
+ GROUP_3_PERMISSION_2_UNRESTRICTED to false,
+ GROUP_2_PERMISSION_2_RESTRICTED to false,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ doAndWaitForWindowTransition { clickPermissionRequestDenyButton() }
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+
+ requestAppPermissionsAndAssertResult(
+ GROUP_3_PERMISSION_1_UNRESTRICTED to true,
+ GROUP_3_PERMISSION_2_UNRESTRICTED to true,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() }
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun grantDialogBlocksRestrictedGroupsThenRequestsUnrestrictedHighPriorityGroups() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+
+ requestAppPermissionsAndAssertResult(
+ GROUP_3_PERMISSION_1_UNRESTRICTED to true,
+ GROUP_2_PERMISSION_1_RESTRICTED to false,
+ GROUP_1_PERMISSION_1_RESTRICTED to false,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() }
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @Test
+ fun grantDialogBlocksRestrictedGroupsThenRequestsUnrestrictedLowPriorityGroups() {
+ installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms(
+ APP_APK_NAME_LATEST
+ )
+
+ requestAppPermissionsAndAssertResult(
+ GROUP_4_PERMISSION_1_UNRESTRICTED to true,
+ GROUP_2_PERMISSION_1_RESTRICTED to false,
+ GROUP_1_PERMISSION_1_RESTRICTED to false,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ doAndWaitForWindowTransition { click(By.res(ALERT_DIALOG_OK_BUTTON), TIMEOUT_MILLIS) }
+ doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() }
+ }
+ assertTrue(isClearRestrictionAllowed(APP_PACKAGE_NAME))
+ }
+
+ private fun isClearRestrictionAllowed(packageName: String) = callWithShellPermissionIdentity {
+ ecm.isClearRestrictionAllowed(packageName)
+ }
+
+ private fun assertNoEcmDialogShown() {
+ assertNull(
+ "expected to not see dialog",
+ waitFindObjectOrNull(By.res(ALERT_DIALOG_OK_BUTTON), UNEXPECTED_TIMEOUT_MILLIS.toLong())
+ )
+ }
+
+ companion object {
+ private const val GROUP_1_PERMISSION_1_RESTRICTED = Manifest.permission.CALL_PHONE
+ private const val GROUP_1_PERMISSION_2_RESTRICTED = Manifest.permission.READ_PHONE_STATE
+ private const val GROUP_2_PERMISSION_1_RESTRICTED = Manifest.permission.SEND_SMS
+ private const val GROUP_2_PERMISSION_2_RESTRICTED = Manifest.permission.READ_SMS
+ private const val GROUP_3_PERMISSION_1_UNRESTRICTED =
+ Manifest.permission.ACCESS_FINE_LOCATION
+ private const val GROUP_3_PERMISSION_2_UNRESTRICTED =
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ private const val GROUP_4_PERMISSION_1_UNRESTRICTED = Manifest.permission.BODY_SENSORS
+
+ private const val NON_PROTECTED_SETTING = "example_setting_which_is_not_protected"
+ private const val PROTECTED_SETTING = "android:bind_accessibility_service"
+
+ @Throws(PackageManager.NameNotFoundException::class)
+ private fun setAppEcmState(
+ context: Context,
+ appOpsManager: AppOpsManager,
+ packageName: String,
+ mode: Int
+ ) =
+ appOpsManager.setMode(
+ AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS,
+ getPackageUid(context, packageName),
+ packageName,
+ mode
+ )
+
+ @Throws(PackageManager.NameNotFoundException::class)
+ private fun getAppEcmState(
+ context: Context,
+ appOpsManager: AppOpsManager,
+ packageName: String
+ ) =
+ appOpsManager.noteOpNoThrow(
+ AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS,
+ getPackageUid(context, packageName),
+ packageName,
+ context.attributionTag,
+ /* message */ null
+ )
+
+ @Throws(PackageManager.NameNotFoundException::class)
+ private fun getPackageUid(context: Context, packageName: String) =
+ getApplicationInfoAsUser(context, packageName).uid
+
+ @Throws(PackageManager.NameNotFoundException::class)
+ private fun getApplicationInfoAsUser(context: Context, packageName: String) =
+ packageManager.getApplicationInfoAsUser(
+ packageName,
+ /* flags */ 0,
+ Process.myUserHandle()
+ )
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/LocationAccuracyTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/LocationAccuracyTest.kt
new file mode 100644
index 000000000..176010cf5
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/LocationAccuracyTest.kt
@@ -0,0 +1,164 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import androidx.test.filters.FlakyTest
+import androidx.test.uiautomator.By
+import com.android.modules.utils.build.SdkLevel
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+
+@FlakyTest
+class LocationAccuracyTest : BaseUsePermissionTest() {
+
+ companion object {
+ private const val LOCATION_ACCURACY_PRECISE_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/permission_location_accuracy_radio_fine"
+ private const val LOCATION_ACCURACY_COARSE_RADIO_BUTTON =
+ "com.android.permissioncontroller:id/permission_location_accuracy_radio_coarse"
+ private const val LOCATION_ACCURACY_PRECISE_ONLY_VIEW =
+ "com.android.permissioncontroller:id/permission_location_accuracy_fine_only"
+ private const val LOCATION_ACCURACY_COARSE_ONLY_VIEW =
+ "com.android.permissioncontroller:id/permission_location_accuracy_coarse_only"
+ }
+
+ @Before
+ fun setup() {
+ assumeTrue("Location Accuracy is only available on S+", SdkLevel.isAtLeastS())
+ assumeFalse(isAutomotive)
+ assumeFalse(isTv)
+ assumeFalse(isWatch)
+ }
+
+ @Test
+ fun testCoarsePermissionIsGranted() {
+ installPackage(APP_APK_PATH_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to false,
+ ACCESS_COARSE_LOCATION to true
+ ) {
+ clickCoarseLocationRadioButton()
+ clickPreciseLocationRadioButton()
+ clickCoarseLocationRadioButton()
+ clickPermissionRequestAllowForegroundButton()
+ }
+ }
+
+ @Test
+ fun testPrecisePermissionIsGranted() {
+ installPackage(APP_APK_PATH_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to true,
+ ACCESS_COARSE_LOCATION to true
+ ) {
+ clickPreciseLocationRadioButton()
+ clickCoarseLocationRadioButton()
+ clickPreciseLocationRadioButton()
+ clickPermissionRequestAllowForegroundButton()
+ }
+ }
+
+ @Test
+ fun testPermissionUpgradeFlow() {
+ installPackage(APP_APK_PATH_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to false,
+ ACCESS_COARSE_LOCATION to true
+ ) {
+ clickCoarseLocationRadioButton()
+ clickPreciseLocationRadioButton()
+ clickCoarseLocationRadioButton()
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ // now request again to change to precise location
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to true,
+ ACCESS_COARSE_LOCATION to true
+ ) {
+ clickPreciseLocationOnlyView()
+ clickPermissionRequestAllowForegroundButton()
+ }
+ }
+
+ @Test
+ fun testCoarseRequestAndGrant() {
+ installPackage(APP_APK_PATH_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(ACCESS_COARSE_LOCATION to true) {
+ clickCoarseLocationOnlyView()
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+ }
+
+ @Test
+ fun testPreSAppsAutograntFineIfCoarseGranted() {
+ installPackage(APP_APK_PATH_30)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ requestAppPermissionsAndAssertResult(ACCESS_COARSE_LOCATION to true) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to true,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ private fun clickPreciseLocationRadioButton() {
+ click(By.res(LOCATION_ACCURACY_PRECISE_RADIO_BUTTON))
+ }
+
+ private fun clickCoarseLocationRadioButton() {
+ click(By.res(LOCATION_ACCURACY_COARSE_RADIO_BUTTON))
+ }
+
+ private fun clickPreciseLocationOnlyView() {
+ click(By.res(LOCATION_ACCURACY_PRECISE_ONLY_VIEW))
+ }
+
+ private fun clickCoarseLocationOnlyView() {
+ click(By.res(LOCATION_ACCURACY_COARSE_ONLY_VIEW))
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/LocationProviderInterceptDialogTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/LocationProviderInterceptDialogTest.kt
new file mode 100644
index 000000000..e7920edfd
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/LocationProviderInterceptDialogTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.app.Activity
+import android.app.AppOpsManager
+import android.content.ComponentName
+import android.content.Intent
+import android.location.LocationManager
+import android.os.Build
+import android.permission.cts.MtsIgnore
+import android.permission.cts.PermissionUtils
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.AppOpsUtils
+import com.android.compatibility.common.util.CddTest
+import com.android.compatibility.common.util.SystemUtil
+import java.util.concurrent.TimeUnit
+import org.junit.Assert
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+
+private const val EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"
+private const val ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"
+
+/**
+ * Tests that LocationProviderInterceptDialog (a warning dialog) shows when attempting to view the
+ * location permission for location a service provider app (e.g., usually GMS, but we use a custom
+ * app in this test).
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+@FlakyTest
+@CddTest(requirement = "9.1/C-0-1")
+class LocationProviderInterceptDialogTest : BaseUsePermissionTest() {
+ @Before
+ fun setup() {
+ assumeFalse(isAutomotive)
+ assumeFalse(isTv)
+ assumeFalse(isWatch)
+ installPackage(MIC_LOCATION_PROVIDER_APP_APK_PATH, grantRuntimePermissions = true)
+ AppOpsUtils.setOpMode(
+ MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME,
+ AppOpsManager.OPSTR_MOCK_LOCATION,
+ AppOpsManager.MODE_ALLOWED
+ )
+ enableMicrophoneAppAsLocationProvider()
+ }
+
+ @Test
+ @Ignore("b/288471744")
+ @MtsIgnore(bugId = 288471744)
+ fun clickLocationPermission_showDialog_clickOk() {
+ openPermissionScreenForApp()
+ clickAndWaitForWindowTransition(By.text("Location"))
+ findView(By.textContains("Location access can be modified from location settings"), true)
+ click(By.res(OK_BUTTON_RES))
+ }
+
+ @Test
+ @Ignore("b/288471744")
+ @MtsIgnore(bugId = 288471744)
+ fun clickLocationPermission_showDialog_clickLocationAccess() {
+ openPermissionScreenForApp()
+ clickAndWaitForWindowTransition(By.text("Location"))
+ findView(By.textContains("Location access can be modified from location settings"), true)
+ clickAndWaitForWindowTransition(By.res(LOCATION_ACCESS_BUTTON_RES))
+ findView(By.res(USE_LOCATION_LABEL_ID), true)
+ }
+
+ @Test
+ @Ignore("b/288471744")
+ @MtsIgnore(bugId = 288471744)
+ fun checkRestrictedPermissions() {
+ context.sendBroadcast(
+ Intent(PermissionTapjackingTest.ACTION_SHOW_OVERLAY)
+ .putExtra("package", MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME)
+ .putExtra("permission", "android.permission.BACKGROUND_CAMERA")
+ )
+ }
+
+ private fun openPermissionScreenForApp() {
+ restartPermissionController()
+ doAndWaitForWindowTransition {
+ SystemUtil.runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(ACTION_MANAGE_APP_PERMISSIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra(EXTRA_PACKAGE_NAME, MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME)
+ }
+ )
+ }
+ }
+ }
+
+ private fun restartPermissionController() {
+ PermissionUtils.clearAppState(permissionControllerPackageName)
+ }
+
+ private fun enableMicrophoneAppAsLocationProvider() {
+ val locationManager = context.getSystemService(LocationManager::class.java)!!
+ val future =
+ startActivityForFuture(
+ Intent().apply {
+ component =
+ ComponentName(
+ MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME,
+ "$MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME.AddLocationProviderActivity"
+ )
+ }
+ )
+ val result = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ Assert.assertEquals(Activity.RESULT_OK, result.resultCode)
+ Assert.assertTrue(
+ SystemUtil.callWithShellPermissionIdentity {
+ locationManager.isProviderPackage(MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME)
+ }
+ )
+ }
+
+ companion object {
+ private const val USE_LOCATION_LABEL_ID = "com.android.settings:id/switch_text"
+ private const val MIC_LOCATION_PROVIDER_APP_APK_PATH =
+ "$APK_DIRECTORY/CtsAccessMicrophoneAppLocationProvider.apk"
+ private const val MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME =
+ "android.permissionui.cts.accessmicrophoneapplocationprovider"
+ private const val OK_BUTTON_RES = "android:id/button2"
+ private const val LOCATION_ACCESS_BUTTON_RES = "android:id/button1"
+ private val permissionControllerPackageName =
+ context.packageManager.permissionControllerPackageName
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionTest.kt
new file mode 100644
index 000000000..d41c7454f
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest
+import android.os.Build
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.CddTest
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assume
+import org.junit.Test
+
+/**
+ * Tests media storage supergroup behavior. I.e., on a T+ platform, for legacy (targetSdk<T) apps,
+ * the storage permission groups (STORAGE, AURAL, and VISUAL) form a supergroup, which effectively
+ * treats them as one group and therefore their permission state must always be equal.
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+@CddTest(requirement = "9.1/C-0-1")
+@FlakyTest
+class MediaPermissionTest : BaseUsePermissionTest() {
+ private fun assertStorageAndMediaPermissionState(state: Boolean) {
+ for (permission in STORAGE_AND_MEDIA_PERMISSIONS) {
+ assertAppHasPermission(permission, state)
+ }
+ }
+
+ @Test
+ fun testWhenRESIsGrantedFromGrantDialogThenShouldGrantAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ requestAppPermissionsAndAssertResult(Manifest.permission.READ_EXTERNAL_STORAGE to true) {
+ clickPermissionRequestAllowButton()
+ }
+ assertStorageAndMediaPermissionState(true)
+ }
+
+ @Test
+ fun testWhenRESIsGrantedManuallyThenShouldGrantAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ grantAppPermissionsByUi(Manifest.permission.READ_EXTERNAL_STORAGE)
+ assertStorageAndMediaPermissionState(true)
+ }
+
+ @Test
+ fun testWhenAuralIsGrantedManuallyThenShouldGrantAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ grantAppPermissionsByUi(Manifest.permission.READ_MEDIA_AUDIO)
+ assertStorageAndMediaPermissionState(true)
+ }
+
+ @Test
+ fun testWhenVisualIsGrantedManuallyThenShouldGrantAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ grantAppPermissionsByUi(Manifest.permission.READ_MEDIA_VIDEO)
+ assertStorageAndMediaPermissionState(true)
+ }
+
+ @Test
+ fun testWhenRESIsDeniedFromGrantDialogThenShouldDenyAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ requestAppPermissionsAndAssertResult(Manifest.permission.READ_EXTERNAL_STORAGE to false) {
+ clickPermissionRequestDenyButton()
+ }
+ assertStorageAndMediaPermissionState(false)
+ }
+
+ @Test
+ fun testWhenRESIsDeniedManuallyThenShouldDenyAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ grantAppPermissionsByUi(Manifest.permission.READ_EXTERNAL_STORAGE)
+ assertStorageAndMediaPermissionState(true)
+ revokeAppPermissionsByUi(Manifest.permission.READ_EXTERNAL_STORAGE)
+ assertStorageAndMediaPermissionState(false)
+ }
+
+ @Test
+ fun testWhenAuralIsDeniedManuallyThenShouldDenyAllPermissions() {
+ installPackage(APP_APK_PATH_23)
+ grantAppPermissionsByUi(Manifest.permission.READ_MEDIA_AUDIO)
+ revokeAppPermissionsByUi(Manifest.permission.READ_MEDIA_AUDIO)
+ assertStorageAndMediaPermissionState(false)
+ }
+
+ @Test
+ fun testWhenVisualIsDeniedManuallyThenShouldDenyAllPermissions() {
+ // TODO: Re-enable after b/239249703 is fixed
+ Assume.assumeFalse("skip on TV due to flaky", isTv)
+ installPackage(APP_APK_PATH_23)
+ grantAppPermissionsByUi(Manifest.permission.READ_MEDIA_VIDEO)
+ revokeAppPermissionsByUi(Manifest.permission.READ_MEDIA_VIDEO)
+ assertStorageAndMediaPermissionState(false)
+ }
+
+ @Test
+ fun testWhenA33AppRequestsStorageThenNoDialogAndNoGrant() {
+ installPackage(APP_APK_PATH_MEDIA_PERMISSION_33_WITH_STORAGE)
+ requestAppPermissions(
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ waitForWindowTransition = false
+ ) {}
+ assertStorageAndMediaPermissionState(false)
+ }
+
+ @Test
+ fun testWhenA33AppRequestsAuralThenDialogAndGrant() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissions(Manifest.permission.READ_MEDIA_AUDIO) {
+ clickPermissionRequestAllowButton()
+ }
+ assertAppHasPermission(Manifest.permission.READ_EXTERNAL_STORAGE, false)
+ assertAppHasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, false)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_AUDIO, true)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_VIDEO, false)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_IMAGES, false)
+ }
+
+ @Test
+ fun testWhenA33AppRequestsVisualThenDialogAndGrant() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissions(
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.READ_MEDIA_IMAGES
+ ) {
+ if (isPhotoPickerPermissionPromptEnabled()) {
+ clickPermissionRequestAllowAllButton()
+ } else {
+ clickPermissionRequestAllowButton()
+ }
+ }
+ assertAppHasPermission(Manifest.permission.READ_EXTERNAL_STORAGE, false)
+ assertAppHasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, false)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_AUDIO, false)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_VIDEO, true)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_IMAGES, true)
+ }
+
+ @Test
+ fun testWhenA30AppRequestsStorageWhenMediaPermsHaveRWRFlag() {
+ installPackage(APP_APK_PATH_30)
+
+ requestAppPermissionsAndAssertResult(Manifest.permission.READ_EXTERNAL_STORAGE to true) {
+ clickPermissionRequestAllowButton()
+ }
+
+ fun setRevokeWhenRequested(permission: String) =
+ SystemUtil.runShellCommandOrThrow(
+ "pm set-permission-flags android.permissionui.cts.usepermission " +
+ permission +
+ " revoke-when-requested"
+ )
+ setRevokeWhenRequested("android.permission.READ_MEDIA_AUDIO")
+ setRevokeWhenRequested("android.permission.READ_MEDIA_VIDEO")
+ setRevokeWhenRequested("android.permission.READ_MEDIA_IMAGES")
+
+ requestAppPermissionsAndAssertResult(
+ Manifest.permission.READ_EXTERNAL_STORAGE to true,
+ waitForWindowTransition = false
+ ) {
+ // No dialog should appear
+ }
+
+ assertAppHasPermission(Manifest.permission.READ_EXTERNAL_STORAGE, true)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_AUDIO, true)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_VIDEO, true)
+ assertAppHasPermission(Manifest.permission.READ_MEDIA_IMAGES, true)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionUpgradeTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionUpgradeTest.kt
new file mode 100644
index 000000000..40c09ea8c
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/MediaPermissionUpgradeTest.kt
@@ -0,0 +1,60 @@
+/*
+ * 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 android.permissionui.cts
+
+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.os.Build
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.CddTest
+import org.junit.Test
+
+/** Tests media storage permission behavior upon app upgrade. */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+@CddTest(requirement = "9.1/C-0-1")
+@FlakyTest
+class MediaPermissionUpgradeTest : BaseUsePermissionTest() {
+ @Test
+ fun testAfterUpgradeToTiramisuThenNoGrantDialogShownForMediaPerms() {
+ // Install 32
+ installPackage(APP_APK_PATH_32)
+
+ // Request STORAGE, and click allow
+ requestAppPermissionsAndAssertResult(
+ READ_EXTERNAL_STORAGE to true,
+ waitForWindowTransition = !isWatch
+ ) {
+ clickPermissionRequestAllowButton()
+ }
+
+ // Upgrade 32 -> 33
+ installPackage(APP_APK_PATH_LATEST, reinstall = true)
+
+ // Request READ_MEDIA_*
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_AUDIO to true,
+ READ_MEDIA_VIDEO to true,
+ READ_MEDIA_IMAGES to true,
+ waitForWindowTransition = false
+ ) {
+ // Don't click any grant dialog buttons because no grant dialog should appear
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/NoPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/NoPermissionTest.kt
new file mode 100644
index 000000000..a5d428812
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/NoPermissionTest.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts
+
+import android.app.Activity
+import androidx.test.filters.FlakyTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.modules.utils.build.SdkLevel
+import org.junit.Assume
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@FlakyTest
+class NoPermissionTest : BaseUsePermissionTest() {
+ @Test
+ fun testStartActivity22() {
+ Assume.assumeFalse(SdkLevel.isAtLeastT())
+ installPackage(APP_APK_PATH_22_NONE)
+
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {}
+
+ clearTargetSdkWarning()
+ }
+
+ @Test
+ fun testStartActivityLatest() {
+ installPackage(APP_APK_PATH_LATEST_NONE)
+
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {}
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt
new file mode 100644
index 000000000..9b72d1706
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/NotificationPermissionTest.kt
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest.permission.POST_NOTIFICATIONS
+import android.Manifest.permission.RECORD_AUDIO
+import android.app.ActivityOptions
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Context.RECEIVER_EXPORTED
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.os.Build
+import android.os.UserHandle
+import android.provider.Settings
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import java.util.concurrent.CountDownLatch
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+
+const val EXTRA_CREATE_CHANNELS = "extra_create"
+const val EXTRA_REQUEST_OTHER_PERMISSIONS = "extra_request_permissions"
+const val EXTRA_REQUEST_NOTIF_PERMISSION = "extra_request_notif_permission"
+const val EXTRA_START_SECOND_ACTIVITY = "extra_start_second_activity"
+const val EXTRA_START_SECOND_APP = "extra_start_second_app"
+const val ACTIVITY_LABEL = "CreateNotif"
+const val SECOND_ACTIVITY_LABEL = "EmptyActivity"
+const val ALLOW = "to send you"
+const val INTENT_ACTION = "usepermission.createchannels.MAIN"
+const val BROADCAST_ACTION = "usepermission.createchannels.BROADCAST"
+const val NOTIFICATION_PERMISSION_ENABLED = "notification_permission_enabled"
+const val EXPECTED_TIMEOUT_MS = 2000L
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+@FlakyTest
+class NotificationPermissionTest : BaseUsePermissionTest() {
+
+ private val cr = callWithShellPermissionIdentity {
+ context.createContextAsUser(UserHandle.SYSTEM, 0).contentResolver
+ }
+ private var previousEnableState = -1
+ private var countDown: CountDownLatch = CountDownLatch(1)
+ private var allowedGroups = listOf<String>()
+ private val receiver: BroadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ allowedGroups =
+ intent?.getStringArrayListExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS
+ )
+ ?: emptyList()
+ countDown.countDown()
+ }
+ }
+
+ @Before
+ fun setLatchAndEnablePermission() {
+ // b/220968160: Notification permission is not enabled on TV devices.
+ assumeFalse(isTv)
+ runWithShellPermissionIdentity {
+ previousEnableState = Settings.Secure.getInt(cr, NOTIFICATION_PERMISSION_ENABLED, 0)
+ Settings.Secure.putInt(cr, NOTIFICATION_PERMISSION_ENABLED, 1)
+ }
+ countDown = CountDownLatch(1)
+ allowedGroups = listOf()
+ context.registerReceiver(receiver, IntentFilter(BROADCAST_ACTION), RECEIVER_EXPORTED)
+ }
+
+ @After
+ fun resetPermissionAndRemoveReceiver() {
+ if (previousEnableState >= 0) {
+ runWithShellPermissionIdentity {
+ Settings.Secure.putInt(cr, NOTIFICATION_PERMISSION_ENABLED, previousEnableState)
+ }
+ context.unregisterReceiver(receiver)
+ }
+ }
+
+ @Test
+ fun notificationPermissionAddedForLegacyApp() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ runWithShellPermissionIdentity {
+ Assert.assertTrue(
+ "SDK < 32 apps should have POST_NOTIFICATIONS added implicitly",
+ context.packageManager
+ .getPackageInfo(APP_PACKAGE_NAME, PackageManager.GET_PERMISSIONS)
+ .requestedPermissions!!
+ .contains(POST_NOTIFICATIONS)
+ )
+ }
+ }
+
+ @Test
+ fun notificationPermissionIsNotImplicitlyAddedTo33Apps() {
+ installPackage(APP_APK_PATH_LATEST_NONE, expectSuccess = true)
+ runWithShellPermissionIdentity {
+ val requestedPerms =
+ context.packageManager
+ .getPackageInfo(APP_PACKAGE_NAME, PackageManager.GET_PERMISSIONS)
+ .requestedPermissions
+ Assert.assertTrue(
+ "SDK >= 33 apps should NOT have POST_NOTIFICATIONS added implicitly",
+ requestedPerms == null || !requestedPerms.contains(POST_NOTIFICATIONS)
+ )
+ }
+ }
+
+ @Test
+ fun notificationPromptShowsForLegacyAppAfterCreatingNotificationChannels() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp()
+ clickPermissionRequestAllowButton()
+ }
+
+ @Test
+ fun notificationPromptShowsForLegacyAppWithNotificationChannelsOnStart() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ // create channels, then leave the app
+ launchApp()
+ killTestApp()
+ launchApp()
+ waitFindObject(By.textContains(ALLOW))
+ clickPermissionRequestAllowButton()
+ }
+
+ @Test
+ fun notificationPromptDoesNotShowForLegacyAppWithNoNotificationChannels_onLaunch() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(createChannels = false)
+ assertDialogNotShowing()
+ }
+ @Test
+ fun notificationPromptDoesNotShowForNonLauncherIntentCategoryLaunches_onChannelCreate() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(launcherCategory = false)
+ assertDialogNotShowing()
+ }
+
+ @Test
+ fun notificationPromptDoesNotShowForNonLauncherIntentCategoryLaunches_onLaunch() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ // create channels, then leave the app
+ launchApp()
+ killTestApp()
+ launchApp(launcherCategory = false)
+ assertDialogNotShowing()
+ }
+
+ @Test
+ fun notificationPromptDoesNotShowForNonMainIntentActionLaunches_onLaunch() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ // create channels, then leave the app
+ launchApp()
+ killTestApp()
+ launchApp(intentAction = INTENT_ACTION)
+ assertDialogNotShowing()
+ }
+
+ @Test
+ fun notificationPromptDoesNotShowForNonMainIntentActionLaunches_onChannelCreate() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(intentAction = INTENT_ACTION)
+ assertDialogNotShowing()
+ }
+
+ @Test
+ fun notificationPromptShowsIfActivityOptionSet() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ // create channels, then leave the app
+ launchApp()
+ killTestApp()
+ launchApp(intentAction = INTENT_ACTION, isEligibleForPromptOption = true)
+ clickPermissionRequestAllowButton()
+ }
+
+ @Test
+ fun notificationPromptShownForSubsequentStartsIfTaskStartWasLauncher() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(startSecondActivity = true)
+ if (isAutomotive || isWatch) {
+ waitFindObject(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)))
+ } else {
+ waitFindObject(By.res(ALLOW_BUTTON))
+ }
+ pressBack()
+ clickPermissionRequestAllowButton()
+ }
+
+ @Test
+ fun notificationPromptNotShownForSubsequentStartsIfTaskStartWasNotLauncher() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(intentAction = INTENT_ACTION, startSecondActivity = true)
+ assertDialogNotShowing()
+ }
+
+ @Test
+ fun notificationPromptShownForChannelCreateInSecondActivityIfTaskStartWasLauncher() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(startSecondActivity = true, createChannels = false)
+ clickPermissionRequestAllowButton()
+ }
+
+ @Test
+ fun notificationPromptNotShownForChannelCreateInSecondActivityIfTaskStartWasntLauncher() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(intentAction = INTENT_ACTION, startSecondActivity = true, createChannels = false)
+ assertDialogNotShowing()
+ }
+
+ @Test
+ fun notificationPromptNotShownForSubsequentStartsIfSubsequentIsDifferentPkg() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ installPackage(APP_APK_PATH_OTHER_APP, expectSuccess = true)
+ // perform a launcher start, then start a secondary app
+ launchApp(startSecondaryAppAndCreateChannelsAfterSecondStart = true)
+ try {
+ // Watch does not have app bar
+ if (!isWatch) {
+ waitFindObject(By.textContains(SECOND_ACTIVITY_LABEL))
+ }
+ assertDialogNotShowing()
+ } finally {
+ uninstallPackage(OTHER_APP_PACKAGE_NAME)
+ }
+ }
+
+ @Test
+ fun notificationGrantedOnLegacyGrant() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp()
+ clickPermissionRequestAllowButton()
+ assertAppPermissionGrantedState(POST_NOTIFICATIONS, granted = true)
+ }
+
+ @Test
+ fun nonSystemServerPackageCannotShowPromptForOtherPackage() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ runWithShellPermissionIdentity {
+ val grantPermission = Intent(PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER)
+ grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ grantPermission.putExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES,
+ arrayOf(POST_NOTIFICATIONS)
+ )
+ grantPermission.setPackage(context.packageManager.permissionControllerPackageName)
+ grantPermission.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ context.startActivity(grantPermission)
+ }
+ try {
+ clickPermissionRequestAllowButton(timeoutMillis = EXPECTED_TIMEOUT_MS)
+ Assert.fail("Expected not to find permission request dialog")
+ } catch (expected: RuntimeException) {
+ // Do nothing
+ }
+ }
+
+ @Test
+ fun mergeAppPermissionRequestIntoNotificationAndVerifyResult() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp()
+ findPermissionRequestAllowButton()
+ // Notification dialog is showing, trigger RECORD_AUDIO check, and wait until it has been
+ // requested
+ val intent = createIntent(requestPermissions = true, intentAction = BROADCAST_ACTION)
+ context.sendBroadcast(intent)
+ countDown.await()
+ Thread.sleep(1000)
+ // reset countDownLatch
+ countDown = CountDownLatch(1)
+
+ clickPermissionRequestAllowButton()
+ assertAppPermissionGrantedState(POST_NOTIFICATIONS, granted = true)
+ clickPermissionRequestAllowForegroundButton()
+ assertAppPermissionGrantedState(RECORD_AUDIO, granted = true)
+ countDown.await()
+ // Result should contain only the microphone request
+ Assert.assertEquals(listOf(RECORD_AUDIO), allowedGroups)
+ }
+
+ @Test
+ fun mergeNotificationRequestIntoAppPermissionRequestAndVerifyResult() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(createChannels = false, requestPermissions = true)
+ findPermissionRequestAllowForegroundButton()
+ // Microphone dialog is showing, trigger Notification check, and wait until it has been
+ // requested
+ val intent = createIntent(createChannels = true, intentAction = BROADCAST_ACTION)
+ context.sendBroadcast(intent)
+ countDown.await()
+ Thread.sleep(1000)
+ // reset countDownLatch
+ countDown = CountDownLatch(1)
+
+ clickPermissionRequestAllowForegroundButton()
+ assertAppPermissionGrantedState(RECORD_AUDIO, granted = true)
+ clickPermissionRequestAllowButton()
+ assertAppPermissionGrantedState(POST_NOTIFICATIONS, granted = true)
+ countDown.await()
+ // Result should contain only the microphone request
+ Assert.assertEquals(listOf(RECORD_AUDIO), allowedGroups)
+ }
+
+ @Test
+ fun legacyAppCannotExplicitlyRequestNotifications() {
+ installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
+ launchApp(createChannels = false, requestNotificationPermission = true)
+ try {
+ clickPermissionRequestAllowButton(timeoutMillis = EXPECTED_TIMEOUT_MS)
+ Assert.fail("Expected not to find permission request dialog")
+ } catch (expected: RuntimeException) {
+ // Do nothing
+ }
+ }
+
+ private fun assertAppPermissionGrantedState(permission: String, granted: Boolean) {
+ SystemUtil.eventually {
+ runWithShellPermissionIdentity {
+ Assert.assertEquals(
+ "Expected $permission to be granted",
+ context.packageManager.checkPermission(permission, APP_PACKAGE_NAME),
+ PERMISSION_GRANTED
+ )
+ }
+ }
+ }
+
+ private fun createIntent(
+ createChannels: Boolean = true,
+ requestNotificationPermission: Boolean = false,
+ requestPermissions: Boolean = false,
+ launcherCategory: Boolean = true,
+ intentAction: String = Intent.ACTION_MAIN,
+ startSecondActivity: Boolean = false,
+ startSecondaryAppAndCreateChannelsAfterSecondStart: Boolean = false
+ ): Intent {
+ val intent =
+ if (intentAction == Intent.ACTION_MAIN && launcherCategory) {
+ packageManager.getLaunchIntentForPackage(APP_PACKAGE_NAME)!!
+ } else {
+ Intent(intentAction)
+ }
+
+ intent.`package` = APP_PACKAGE_NAME
+ intent.putExtra(EXTRA_CREATE_CHANNELS, createChannels)
+ intent.putExtra(EXTRA_REQUEST_OTHER_PERMISSIONS, requestPermissions)
+ intent.putExtra(EXTRA_REQUEST_NOTIF_PERMISSION, requestNotificationPermission)
+ intent.putExtra(EXTRA_START_SECOND_ACTIVITY, startSecondActivity)
+ intent.putExtra(EXTRA_START_SECOND_APP, startSecondaryAppAndCreateChannelsAfterSecondStart)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ return intent
+ }
+
+ private fun launchApp(
+ createChannels: Boolean = true,
+ requestNotificationPermission: Boolean = false,
+ requestPermissions: Boolean = false,
+ launcherCategory: Boolean = true,
+ intentAction: String = Intent.ACTION_MAIN,
+ isEligibleForPromptOption: Boolean = false,
+ startSecondActivity: Boolean = false,
+ startSecondaryAppAndCreateChannelsAfterSecondStart: Boolean = false
+ ) {
+ val intent =
+ createIntent(
+ createChannels,
+ requestNotificationPermission,
+ requestPermissions,
+ launcherCategory,
+ intentAction,
+ startSecondActivity,
+ startSecondaryAppAndCreateChannelsAfterSecondStart
+ )
+
+ val options = ActivityOptions.makeBasic()
+ options.isEligibleForLegacyPermissionPrompt = isEligibleForPromptOption
+ doAndWaitForWindowTransition { context.startActivity(intent, options.toBundle()) }
+
+ // Watch does not have app bar
+ if (!isWatch) {
+ waitFindObject(By.textContains(ACTIVITY_LABEL))
+ }
+ }
+
+ private fun assertDialogNotShowing(timeoutMillis: Long = EXPECTED_TIMEOUT_MS) {
+ try {
+ clickPermissionRequestAllowButton(timeoutMillis)
+ Assert.fail("Expected not to find permission request dialog")
+ } catch (expected: RuntimeException) {
+ // Do nothing
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionDecisionsTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionDecisionsTest.kt
new file mode 100644
index 000000000..495648b55
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionDecisionsTest.kt
@@ -0,0 +1,131 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest
+import android.content.Intent
+import android.os.Build
+import android.permission.PermissionManager
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assert.assertNull
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+@FlakyTest
+class PermissionDecisionsTest : BaseUsePermissionTest() {
+
+ companion object {
+ const val ASSERT_ABSENT_SELECTOR_TIMEOUT_MS = 500L
+ }
+
+ // Permission decisions has only been implemented on Auto
+ @Before
+ fun assumeAuto() {
+ assumeTrue(isAutomotive)
+ }
+
+ @Test
+ fun testAcceptPermissionDialogShowsDecisionWithGrantedAccess() {
+ installPackage(APP_APK_PATH_30_WITH_BACKGROUND)
+ requestAppPermissionsAndAssertResult(Manifest.permission.ACCESS_FINE_LOCATION to true) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ openPermissionDecisions()
+ waitFindObject(
+ By.hasChild(By.text("You gave $APP_PACKAGE_NAME access to location"))
+ .hasChild(By.text("Today"))
+ )
+ }
+
+ @Test
+ fun testDenyPermissionDialogShowsDecisionWithDeniedAccess() {
+ installPackage(APP_APK_PATH_30_WITH_BACKGROUND)
+ requestAppPermissionsAndAssertResult(Manifest.permission.ACCESS_FINE_LOCATION to false) {
+ clickPermissionRequestDenyButton()
+ }
+
+ openPermissionDecisions()
+ waitFindObject(
+ By.hasChild(By.text("You denied $APP_PACKAGE_NAME access to location"))
+ .hasChild(By.text("Today"))
+ )
+ }
+
+ @Test
+ fun testAppUninstallRemovesDecision() {
+ installPackage(APP_APK_PATH_30_WITH_BACKGROUND)
+ requestAppPermissionsAndAssertResult(Manifest.permission.ACCESS_FINE_LOCATION to false) {
+ clickPermissionRequestDenyButton()
+ }
+ uninstallApp()
+
+ openPermissionDecisions()
+ assertNull(
+ waitFindObjectOrNull(
+ By.hasChild(By.text("You denied $APP_PACKAGE_NAME access to location"))
+ .hasChild(By.text("Today")),
+ ASSERT_ABSENT_SELECTOR_TIMEOUT_MS
+ )
+ )
+ }
+
+ @Test
+ fun testClickOnDecisionAndChangeAccessUpdatesDecision() {
+ installPackage(APP_APK_PATH_30_WITH_BACKGROUND)
+ requestAppPermissionsAndAssertResult(Manifest.permission.ACCESS_FINE_LOCATION to true) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ openPermissionDecisions()
+
+ waitFindObject(
+ By.hasChild(By.text("You gave $APP_PACKAGE_NAME access to location"))
+ .hasChild(By.text("Today"))
+ )
+ .click()
+
+ waitFindObject(By.text(APP_PACKAGE_NAME))
+ waitFindObject(By.text("Location access for this app"))
+
+ // change the permission on the app permission screen and verify that updates the decision
+ // page
+ waitFindObject(By.text("Don’t allow")).click()
+ pressBack()
+ waitFindObject(
+ By.hasChild(By.text("You denied $APP_PACKAGE_NAME access to location"))
+ .hasChild(By.text("Today"))
+ )
+ }
+
+ private fun openPermissionDecisions() {
+ doAndWaitForWindowTransition {
+ SystemUtil.runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(PermissionManager.ACTION_REVIEW_PERMISSION_DECISIONS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
+ }
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionGroupTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionGroupTest.kt
new file mode 100644
index 000000000..b27d9ea69
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionGroupTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import androidx.test.filters.FlakyTest
+import org.junit.Test
+
+/** Runtime permission behavior tests for permission groups. */
+@FlakyTest
+class PermissionGroupTest : BaseUsePermissionTest() {
+ @Test
+ fun testRuntimeGroupGrantExpansion23() {
+ installPackage(APP_APK_PATH_23)
+ testRuntimeGroupGrantExpansion(true)
+ }
+
+ @Test
+ fun testRuntimeGroupGrantExpansion25() {
+ installPackage(APP_APK_PATH_25)
+ testRuntimeGroupGrantExpansion(true)
+ }
+
+ @Test
+ fun testRuntimeGroupGrantExpansion26() {
+ installPackage(APP_APK_PATH_26)
+ testRuntimeGroupGrantExpansion(false)
+ }
+
+ @Test
+ fun testRuntimeGroupGrantExpansion30() {
+ installPackage(APP_APK_PATH_30)
+ testRuntimeGroupGrantExpansion(false)
+ }
+
+ @Test
+ fun testPartiallyGrantedGroupExpansion() {
+ installPackage(APP_APK_PATH_30)
+
+ // Start out without permission
+ assertAppHasPermission(android.Manifest.permission.RECEIVE_SMS, false)
+ assertAppHasPermission(android.Manifest.permission.SEND_SMS, false)
+
+ // Grant only RECEIVE_SMS
+ uiAutomation.grantRuntimePermission(
+ APP_PACKAGE_NAME,
+ android.Manifest.permission.RECEIVE_SMS
+ )
+ assertAppHasPermission(android.Manifest.permission.RECEIVE_SMS, true)
+
+ // Request both permissions, and expect that SEND_SMS is granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.RECEIVE_SMS to true,
+ android.Manifest.permission.SEND_SMS to true,
+ waitForWindowTransition = false
+ ) {}
+
+ assertAppHasPermission(android.Manifest.permission.SEND_SMS, true)
+ }
+
+ private fun testRuntimeGroupGrantExpansion(expectExpansion: Boolean) {
+ // Start out without permission
+ assertAppHasPermission(android.Manifest.permission.RECEIVE_SMS, false)
+ assertAppHasPermission(android.Manifest.permission.SEND_SMS, false)
+
+ // Request only one permission from the 'SMS' permission group at runtime,
+ // but two from this group are <uses-permission> in the manifest
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.RECEIVE_SMS to true,
+ waitForWindowTransition = !isWatch
+ ) {
+ clickPermissionRequestAllowButton()
+ }
+
+ assertAppHasPermission(android.Manifest.permission.SEND_SMS, expectExpansion)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionNoOpGtsTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionNoOpGtsTest.kt
new file mode 100644
index 000000000..1ca319a30
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionNoOpGtsTest.kt
@@ -0,0 +1,29 @@
+/*
+ * 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 android.permissionui.cts
+
+import com.android.compatibility.common.util.CtsDownstreamingTest
+import org.junit.Test
+
+// NoOp test class so that at least one GTS test passes on all platforms.
+// b/235606392 for reference. Will be removed once we move all downstreaming
+// CtsPermissionUiTestCases to GTS.
+@CtsDownstreamingTest
+class PermissionNoOpGtsTest {
+
+ @Test fun shouldAlwaysPass() {}
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionPolicyTest25.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionPolicyTest25.kt
new file mode 100644
index 000000000..3d03b669a
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionPolicyTest25.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.app.Activity
+import android.content.ComponentName
+import android.content.Intent
+import androidx.test.filters.FlakyTest
+import java.util.concurrent.TimeUnit
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+
+/** Tests for the platform permission policy around apps targeting API 25. */
+@FlakyTest
+class PermissionPolicyTest25 : BasePermissionTest() {
+ companion object {
+ const val APP_APK_PATH_25 = "$APK_DIRECTORY/CtsPermissionPolicyApp25.apk"
+ const val APP_PACKAGE_NAME = "android.permissionui.cts.permissionpolicy"
+ }
+
+ @Before
+ fun installApp25() {
+ uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false)
+ installPackage(APP_APK_PATH_25)
+ }
+
+ @After
+ fun uninstallApp() {
+ uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false)
+ }
+
+ @Test
+ fun testNoProtectionFlagsAddedToNonSignatureProtectionPermissions() {
+ val future =
+ startActivityForFuture(
+ Intent().apply {
+ component =
+ ComponentName(
+ APP_PACKAGE_NAME,
+ "$APP_PACKAGE_NAME.TestProtectionFlagsActivity"
+ )
+ }
+ )
+ val result = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ assertEquals(Activity.RESULT_OK, result.resultCode)
+ assertEquals("", result.resultData!!.getStringExtra("$APP_PACKAGE_NAME.ERROR_MESSAGE"))
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt
new file mode 100644
index 000000000..751c56b3c
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationalePermissionGrantDialogTest.kt
@@ -0,0 +1,359 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.Manifest.permission.CAMERA
+import android.os.Build
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.provider.DeviceConfig
+import android.safetylabel.SafetyLabelConstants.PERMISSION_RATIONALE_ENABLED
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.modules.utils.build.SdkLevel
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Permission rationale in Grant Permission Dialog tests. Permission rationale is only available on
+ * U+
+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@FlakyTest
+class PermissionRationalePermissionGrantDialogTest : BaseUsePermissionTest() {
+
+ @get:Rule
+ val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @get:Rule
+ val deviceConfigPermissionRationaleEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PERMISSION_RATIONALE_ENABLED,
+ true.toString()
+ )
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue("Permission rationale is only available on U+", SdkLevel.isAtLeastU())
+ Assume.assumeFalse(isAutomotive)
+ Assume.assumeFalse(isTv)
+ Assume.assumeFalse(isWatch)
+ }
+
+ @Test
+ fun requestLocationPerm_flagDisabled_noPermissionRationale() {
+ setDeviceConfigPrivacyProperty(PERMISSION_RATIONALE_ENABLED, false.toString())
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_apkHasNoInstallSource_noPermissionRationale() {
+ installPackageWithoutInstallSource(APP_APK_PATH_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_noAppMetadata_noPermissionRationale() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_nullAppMetadata_noPermissionRationale() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_emptyAppMetadata_noPermissionRationale() {
+ installPackageWithInstallSourceAndEmptyMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_invalidAppMetadata_noPermissionRationale() {
+ installPackageWithInstallSourceAndInvalidMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_invalidAppMetadataWithoutTopLevelVersion_noPermissionRationale() {
+ installPackageWithInstallSourceAndMetadataWithoutTopLevelVersion(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_invalidAppMetadataWithInvalidTopLevelVersion_noPermissionRationale() {
+ installPackageWithInstallSourceAndMetadataWithInvalidTopLevelVersion(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_invalidAppMetadataWithoutSafetyLabelVersion_noPermissionRationale() {
+ installPackageWithInstallSourceAndMetadataWithoutSafetyLabelVersion(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_invalidAppMetadataWithInvalidSafetyLabelVersion_noPermissionRationale() {
+ installPackageWithInstallSourceAndMetadataWithInvalidSafetyLabelVersion(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestCameraPerm_noPermissionRationale() {
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(CAMERA, false)
+
+ requestAppPermissionsForNoResult(CAMERA) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestCoarseLocationPerm_hasPermissionRationale_packageSourceUnspecified() {
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(true)
+ }
+ }
+
+ @Test
+ fun requestCoarseLocationPerm_hasPermissionRationale_packageSourceStore() {
+ installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(true)
+ }
+ }
+
+ @Test
+ fun requestCoarseLocationPerm_hasPermissionRationale_packageSourceLocalFile() {
+ installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestCoarseLocationPerm_hasPermissionRationale_packageSourceDownloadedFile() {
+ installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestCoarseLocationPerm_hasPermissionRationale_packageSourceOther() {
+ installPackageWithInstallSourceAndMetadataFromOther(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun requestCoarseLocationPerm_hasAslInApk_packageSourceUnspecified() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31_WITH_ASL)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun requestCoarseLocationPerm_hasAslInApk_packageSourceStore() {
+ installPackageWithInstallSourceAndNoMetadataFromStore(APP_APK_NAME_31_WITH_ASL)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun requestCoarseLocationPerm_hasAslInApk_packageSourceLocalFile() {
+ installPackageWithInstallSourceAndNoMetadataFromLocalFile(APP_APK_NAME_31_WITH_ASL)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun requestCoarseLocationPerm_hasAslInApk_packageSourceDownloadedFile() {
+ installPackageWithInstallSourceAndNoMetadataFromDownloadedFile(APP_APK_NAME_31_WITH_ASL)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun requestCoarseLocationPerm_hasAslInApk_packageSourceOther() {
+ installPackageWithInstallSourceAndNoMetadataFromOther(APP_APK_NAME_31_WITH_ASL)
+
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestFineLocationPerm_hasPermissionRationale() {
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(true)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_clicksPermissionRationale_startsPermissionRationaleActivity() {
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {
+ clickPermissionRationaleViewInGrantDialog()
+ assertPermissionRationaleDialogIsVisible(true)
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ @Test
+ fun requestLocationPerm_clicksPermissionRationale_startsPermissionRationaleActivity_comesBack() {
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {
+ clickPermissionRationaleViewInGrantDialog()
+ assertPermissionRationaleDialogIsVisible(true)
+ pressBack()
+ assertPermissionRationaleDialogIsVisible(false)
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(true)
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationaleTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationaleTest.kt
new file mode 100644
index 000000000..e20fdeffd
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionRationaleTest.kt
@@ -0,0 +1,385 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest
+import android.app.ActivityManager
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.provider.DeviceConfig
+import android.safetylabel.SafetyLabelConstants.PERMISSION_RATIONALE_ENABLED
+import android.text.Spanned
+import android.text.style.ClickableSpan
+import android.util.Log
+import android.view.View
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.modules.utils.build.SdkLevel
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/** Permission rationale activity tests. Permission rationale is only available on U+ */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@FlakyTest
+class PermissionRationaleTest : BaseUsePermissionTest() {
+
+ private var activityManager: ActivityManager? = null
+
+ @get:Rule
+ val deviceConfigPermissionRationaleEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PERMISSION_RATIONALE_ENABLED,
+ true.toString()
+ )
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue("Permission rationale is only available on U+", SdkLevel.isAtLeastU())
+ Assume.assumeFalse(isAutomotive)
+ Assume.assumeFalse(isTv)
+ Assume.assumeFalse(isWatch)
+
+ activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+
+ enableComponent(TEST_INSTALLER_ACTIVITY_COMPONENT_NAME)
+
+ installPackageWithInstallSourceAndMetadata(APP_APK_NAME_31)
+
+ assertAppHasPermission(Manifest.permission.ACCESS_FINE_LOCATION, false)
+ }
+
+ @After
+ fun disableTestInstallerActivity() {
+ disableComponent(TEST_INSTALLER_ACTIVITY_COMPONENT_NAME)
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity_failedByNullMetadata() {
+ installPackageWithInstallSourceAndNoMetadata(APP_APK_NAME_31)
+ navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer()
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity_failedByEmptyMetadata() {
+ installPackageWithInstallSourceAndEmptyMetadata(APP_APK_NAME_31)
+ navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer()
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity_failedByNoTopLevelVersion() {
+ installPackageWithInstallSourceAndMetadataWithoutTopLevelVersion(APP_APK_NAME_31)
+ navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer()
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity_failedByInvalidTopLevelVersion() {
+ installPackageWithInstallSourceAndMetadataWithInvalidTopLevelVersion(APP_APK_NAME_31)
+ navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer()
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity_failedByNoSafetyLabelVersion() {
+ installPackageWithInstallSourceAndMetadataWithoutSafetyLabelVersion(APP_APK_NAME_31)
+ navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer()
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity_failedByInvalidSafetyLabelVersion() {
+ installPackageWithInstallSourceAndMetadataWithInvalidSafetyLabelVersion(APP_APK_NAME_31)
+ navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer()
+ }
+
+ @Test
+ fun startsPermissionRationaleActivity() {
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleDialogIsVisible(true)
+ }
+
+ @Test
+ fun linksToInstallSource() {
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleDialogIsVisible(true)
+
+ clickInstallSourceLink()
+
+ eventually {
+ assertStoreLinkClickSuccessful(installerPackageName = TEST_INSTALLER_PACKAGE_NAME)
+ }
+ }
+
+ @Ignore("b/282063206")
+ @Test
+ fun clickLinkToHelpCenter_opensHelpCenter() {
+ Assume.assumeFalse(getPermissionControllerResString(HELP_CENTER_URL_ID).isNullOrEmpty())
+
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleActivityTitleIsVisible(true)
+ assertHelpCenterLinkAvailable(true)
+
+ clickHelpCenterLink()
+
+ eventually({ assertHelpCenterLinkClickSuccessful() }, NEW_WINDOW_TIMEOUT_MILLIS)
+ }
+
+ @Test
+ fun noHelpCenterLinkAvailable_noHelpCenterClickAction() {
+ Assume.assumeTrue(getPermissionControllerResString(HELP_CENTER_URL_ID).isNullOrEmpty())
+
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleActivityTitleIsVisible(true)
+ assertHelpCenterLinkAvailable(false)
+ }
+
+ @Test
+ fun linksToSettings_noOp_dialogsNotClosed() {
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleDialogIsVisible(true)
+
+ clicksSettings_doesNothing_leaves()
+
+ eventually { assertPermissionRationaleDialogIsVisible(true) }
+ }
+
+ @Test
+ fun linksToSettings_grants_dialogsClose() {
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleDialogIsVisible(true)
+
+ clicksSettings_allowsForeground_leaves()
+
+ // Setting, Permission rationale and Grant dialog should be dismissed
+ eventually {
+ assertPermissionSettingsVisible(false)
+ assertPermissionRationaleDialogIsVisible(false)
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+
+ assertAppHasPermission(Manifest.permission.ACCESS_FINE_LOCATION, true)
+ }
+
+ @Test
+ fun linksToSettings_denies_dialogsClose() {
+ navigateToPermissionRationaleActivity()
+
+ assertPermissionRationaleDialogIsVisible(true)
+
+ clicksSettings_denies_leaves()
+
+ // Setting, Permission rationale and Grant dialog should be dismissed
+ eventually {
+ assertPermissionSettingsVisible(false)
+ assertPermissionRationaleDialogIsVisible(false)
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+
+ assertAppHasPermission(Manifest.permission.ACCESS_FINE_LOCATION, false)
+ }
+
+ private fun navigateToPermissionRationaleActivity_failedShowPermissionRationaleContainer() {
+ requestAppPermissionsForNoResult(Manifest.permission.ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(false)
+ }
+ }
+
+ private fun navigateToPermissionRationaleActivity() {
+ requestAppPermissionsForNoResult(Manifest.permission.ACCESS_FINE_LOCATION) {
+ assertPermissionRationaleContainerOnGrantDialogIsVisible(true)
+ clickPermissionRationaleViewInGrantDialog()
+ }
+ }
+
+ private fun clickInstallSourceLink() {
+ findView(By.res(DATA_SHARING_SOURCE_MESSAGE_ID), true)
+
+ eventually {
+ // UiObject2 doesn't expose CharSequence.
+ val node =
+ uiAutomation.rootInActiveWindow
+ .findAccessibilityNodeInfosByViewId(DATA_SHARING_SOURCE_MESSAGE_ID)[0]
+ assertTrue(node.isVisibleToUser)
+ val text = node.text as Spanned
+ val clickableSpan = text.getSpans(0, text.length, ClickableSpan::class.java)[0]
+ // We could pass in null here in Java, but we need an instance in Kotlin.
+ doAndWaitForWindowTransition { clickableSpan.onClick(View(context)) }
+ }
+ }
+
+ private fun clickHelpCenterLink() {
+ findView(By.res(LEARN_MORE_MESSAGE_ID), true)
+
+ eventually {
+ // UiObject2 doesn't expose CharSequence.
+ val node =
+ uiAutomation.rootInActiveWindow
+ .findAccessibilityNodeInfosByViewId(LEARN_MORE_MESSAGE_ID)[0]
+ assertTrue(node.isVisibleToUser)
+ val text = node.text as Spanned
+ val clickableSpan = text.getSpans(0, text.length, ClickableSpan::class.java)[0]
+ // We could pass in null here in Java, but we need an instance in Kotlin.
+ doAndWaitForWindowTransition { clickableSpan.onClick(View(context)) }
+ }
+ }
+
+ private fun clickSettingsLink() {
+ findView(By.res(SETTINGS_MESSAGE_ID), true)
+
+ eventually {
+ // UiObject2 doesn't expose CharSequence.
+ val node =
+ uiAutomation.rootInActiveWindow
+ .findAccessibilityNodeInfosByViewId(SETTINGS_MESSAGE_ID)[0]
+ assertTrue(node.isVisibleToUser)
+ val text = node.text as Spanned
+ val clickableSpan = text.getSpans(0, text.length, ClickableSpan::class.java)[0]
+ // We could pass in null here in Java, but we need an instance in Kotlin.
+ doAndWaitForWindowTransition { clickableSpan.onClick(View(context)) }
+ }
+ }
+
+ private fun clicksSettings_doesNothing_leaves() {
+ clickSettingsLink()
+ eventually { assertPermissionSettingsVisible(true) }
+ pressBack()
+ }
+
+ private fun clicksSettings_allowsForeground_leaves() {
+ clickSettingsLink()
+ eventually { clickAllowForegroundInSettings() }
+ pressBack()
+ }
+
+ private fun clicksSettings_denies_leaves() {
+ clickSettingsLink()
+ eventually { clicksDenyInSettings() }
+ pressBack()
+ }
+
+ private fun assertHelpCenterLinkAvailable(expected: Boolean) {
+ // Message should always be visible
+ findView(By.res(LEARN_MORE_MESSAGE_ID), true)
+
+ // Verify the link is (or isn't) in message
+ eventually {
+ // UiObject2 doesn't expose CharSequence.
+ val node =
+ uiAutomation.rootInActiveWindow
+ .findAccessibilityNodeInfosByViewId(LEARN_MORE_MESSAGE_ID)[0]
+ assertTrue(node.isVisibleToUser)
+ val text = node.text as Spanned
+ val clickableSpans = text.getSpans(0, text.length, ClickableSpan::class.java)
+
+ if (expected) {
+ assertFalse("Expected help center link, but none found", clickableSpans.isEmpty())
+ } else {
+ assertTrue("Expected no links, but found one", clickableSpans.isEmpty())
+ }
+ }
+ }
+
+ private fun assertPermissionSettingsVisible(expected: Boolean) {
+ findView(By.res(DENY_RADIO_BUTTON), expected = expected)
+ }
+
+ private fun assertStoreLinkClickSuccessful(
+ installerPackageName: String,
+ packageName: String? = null
+ ) {
+ SystemUtil.runWithShellPermissionIdentity {
+ val runningTasks = activityManager!!.getRunningTasks(1)
+
+ assertFalse("Expected runningTasks to not be empty", runningTasks.isEmpty())
+
+ val taskInfo = runningTasks[0]
+ val observedIntentAction = taskInfo.baseIntent.action
+ val observedPackageName = taskInfo.baseIntent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)
+ val observedInstallerPackageName = taskInfo.topActivity?.packageName
+
+ assertEquals(
+ "Unexpected intent action",
+ Intent.ACTION_SHOW_APP_INFO,
+ observedIntentAction
+ )
+ assertEquals(
+ "Unexpected installer package name",
+ installerPackageName,
+ observedInstallerPackageName
+ )
+ assertEquals("Unexpected package name", packageName, observedPackageName)
+ }
+ }
+
+ private fun assertHelpCenterLinkClickSuccessful() {
+ SystemUtil.runWithShellPermissionIdentity {
+ val runningTasks = activityManager!!.getRunningTasks(5)
+
+ Log.v(TAG, "# running tasks: ${runningTasks.size}")
+ assertFalse("Expected runningTasks to not be empty", runningTasks.isEmpty())
+
+ runningTasks.forEachIndexed { index, runningTaskInfo ->
+ Log.v(TAG, "task $index ${runningTaskInfo.baseIntent}")
+ }
+
+ val taskInfo = runningTasks[0]
+ val observedIntentAction = taskInfo.baseIntent.action
+ val observedIntentDataString = taskInfo.baseIntent.dataString
+ val observedIntentScheme: String? = taskInfo.baseIntent.scheme
+
+ Log.v(TAG, "task base intent: ${taskInfo.baseIntent}")
+ assertEquals("Unexpected intent action", Intent.ACTION_VIEW, observedIntentAction)
+
+ val expectedUrl = getPermissionControllerResString(HELP_CENTER_URL_ID)!!
+ assertFalse(observedIntentDataString.isNullOrEmpty())
+ assertTrue(observedIntentDataString?.startsWith(expectedUrl) ?: false)
+
+ assertFalse(observedIntentScheme.isNullOrEmpty())
+ assertEquals("https", observedIntentScheme)
+ }
+ }
+
+ companion object {
+ private val TAG = PermissionRationaleTest::class.java.simpleName
+
+ private const val DATA_SHARING_SOURCE_MESSAGE_ID =
+ "com.android.permissioncontroller:id/data_sharing_source_message"
+ private const val LEARN_MORE_MESSAGE_ID =
+ "com.android.permissioncontroller:id/learn_more_message"
+ private const val SETTINGS_MESSAGE_ID =
+ "com.android.permissioncontroller:id/settings_message"
+
+ private const val HELP_CENTER_URL_ID = "data_sharing_help_center_link"
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt
new file mode 100644
index 000000000..a75f08916
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTapjackingTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts
+
+import android.content.ComponentName
+import android.content.Intent
+import androidx.test.filters.FlakyTest
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil
+import java.lang.Exception
+import org.junit.After
+import org.junit.Assert
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+
+/** Tests permission review screen can't be tapjacked */
+@FlakyTest
+class PermissionReviewTapjackingTest : BaseUsePermissionTest() {
+
+ companion object {
+ const val HELPER_APP_OVERLAY = "$APK_DIRECTORY/CtsHelperAppOverlay.apk"
+ private const val HELPER_PACKAGE_NAME = "android.permissionui.cts.helper.overlay"
+ }
+
+ @Before
+ fun installApp22AndApprovePermissionReview() {
+ assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
+ installPackage(APP_APK_PATH_22)
+ installPackage(HELPER_APP_OVERLAY)
+
+ SystemUtil.runShellCommandOrThrow(
+ "appops set $HELPER_PACKAGE_NAME android:system_alert_window allow"
+ )
+ }
+
+ @After
+ fun uninstallPackages() {
+ SystemUtil.runShellCommandOrThrow("pm uninstall $APP_PACKAGE_NAME")
+ SystemUtil.runShellCommandOrThrow("pm uninstall $HELPER_PACKAGE_NAME")
+ }
+
+ @Test
+ fun testOverlaysAreHidden() {
+ context.startActivity(
+ Intent()
+ .setComponent(
+ ComponentName(HELPER_PACKAGE_NAME, "$HELPER_PACKAGE_NAME.OverlayActivity")
+ )
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ findOverlay()
+
+ context.startActivity(
+ Intent()
+ .setComponent(
+ ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.FinishOnCreateActivity")
+ )
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+
+ if (isWatch) {
+ waitFindObject(
+ By.text(getPermissionControllerString("review_button_cancel")),
+ TIMEOUT_MILLIS * 2
+ )
+ } else {
+ waitFindObject(By.res("com.android.permissioncontroller:id/permissions_message"))
+ }
+
+ try {
+ findOverlay()
+ Assert.fail("Overlay was displayed")
+ } catch (e: Exception) {
+ // expected
+ }
+
+ pressHome()
+ findOverlay()
+ }
+
+ private fun findOverlay() = waitFindObject(By.text("Find me!"))
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTest.kt
new file mode 100644
index 000000000..5ca23fea2
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionReviewTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.app.Activity
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.ResultReceiver
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.AndroidJUnit4
+import androidx.test.uiautomator.By
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@FlakyTest
+class PermissionReviewTest : BaseUsePermissionTest() {
+
+ @Before
+ fun assumeNotIndividuallyControlled() {
+ Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+ }
+
+ @Before
+ fun installApp22CalendarOnly() {
+ installPackage(APP_APK_PATH_22_CALENDAR_ONLY)
+ }
+
+ @get:Rule val activityRule = ActivityTestRule(StartForFutureActivity::class.java, false, false)
+
+ @Test
+ fun testDenyCalendarDuringReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
+ // Deny
+ clickPermissionControllerUi(By.text("Calendar"))
+ // Confirm deny
+ click(By.res("android:id/button1"))
+
+ clickPermissionReviewContinue()
+ }
+
+ clearTargetSdkWarning()
+ assertAppHasCalendarAccess(false)
+ }
+
+ @Test
+ fun testDenyGrantCalendarDuringReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
+ // Deny
+ clickPermissionControllerUi(By.text("Calendar"))
+ // Confirm deny
+ click(By.res("android:id/button1"))
+
+ // Grant
+ clickPermissionControllerUi(By.text("Calendar"))
+
+ clickPermissionReviewContinue()
+ }
+
+ clearTargetSdkWarning()
+ assertAppHasCalendarAccess(true)
+ }
+
+ @Test
+ fun testDenyGrantDenyCalendarDuringReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
+ // Deny
+ clickPermissionControllerUi(By.text("Calendar"))
+
+ // Confirm deny
+ click(By.res("android:id/button1"))
+
+ // Grant
+ clickPermissionControllerUi(By.text("Calendar"))
+
+ // Deny
+ clickPermissionControllerUi(By.text("Calendar"))
+
+ clickPermissionReviewContinue()
+ }
+
+ clearTargetSdkWarning()
+ assertAppHasCalendarAccess(false)
+ }
+
+ @Test
+ fun testCancelReview() {
+ // Start APK_22_ONLY_CALENDAR, but cancel review
+ cancelPermissionReview()
+
+ // Start APK_22_ONLY_CALENDAR again, now approve review
+ approvePermissionReview()
+
+ assertAppDoesNotNeedPermissionReview()
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "TIRAMISU")
+ fun testNotificationPermissionAddedToReview() {
+ startAppActivityAndAssertResultCode(Activity.RESULT_CANCELED) {
+ waitFindObject(By.text("Notifications"), 5000L)
+ clickPermissionReviewCancel()
+ }
+ }
+
+ @Test
+ fun testReviewPermissionWhenServiceIsBound() {
+ val results = LinkedBlockingQueue<Int>()
+ // We are starting a activity instead of the service directly, because
+ // the service comes from a different app than the CTS tests.
+ // This app will be considered idle on devices that have idling enabled (automotive),
+ // and the service wouldn't be allowed to be started without the activity.
+ activityRule
+ .launchActivity(null)
+ .startActivity(
+ Intent().apply {
+ component =
+ ComponentName(
+ APP_PACKAGE_NAME,
+ "$APP_PACKAGE_NAME.StartCheckPermissionServiceActivity"
+ )
+ putExtra(
+ "$APP_PACKAGE_NAME.RESULT",
+ object : ResultReceiver(Handler(Looper.getMainLooper())) {
+ override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
+ results.offer(resultCode)
+ }
+ }
+ )
+ putExtra(
+ "$APP_PACKAGE_NAME.PERMISSION",
+ android.Manifest.permission.READ_CALENDAR
+ )
+ }
+ )
+
+ // Service is not started before permission are reviewed
+ assertNull(results.poll(UNEXPECTED_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS))
+
+ clickPermissionReviewContinueAndClearSdkWarning()
+
+ // Service should be started after permission review
+ assertEquals(
+ PackageManager.PERMISSION_GRANTED,
+ results.poll(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ )
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
new file mode 100644
index 000000000..d509add3a
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionSplitTest.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.os.Build
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+
+/** Runtime permission behavior tests for permission splits. */
+@FlakyTest
+class PermissionSplitTest : BaseUsePermissionTest() {
+ @Before
+ fun assumeNotTv() {
+ assumeFalse(isTv)
+ }
+
+ @Test
+ fun testPermissionSplit28() {
+ installPackage(APP_APK_PATH_28)
+ testLocationPermissionSplit(true)
+ }
+
+ @Test
+ fun testPermissionNotSplit29() {
+ installPackage(APP_APK_PATH_29)
+ testLocationPermissionSplit(false)
+ }
+
+ @Test
+ fun testPermissionNotSplit30() {
+ installPackage(APP_APK_PATH_30)
+ testLocationPermissionSplit(false)
+ }
+
+ @Test
+ fun testPermissionNotSplitLatest() {
+ installPackage(APP_APK_PATH_LATEST)
+ testLocationPermissionSplit(false)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @Test
+ fun testBodySensorSplit() {
+ installPackage(APP_APK_PATH_31)
+ testBodySensorPermissionSplit(true)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @Test
+ fun testBodySensorSplit32() {
+ installPackage(APP_APK_PATH_32)
+ testBodySensorPermissionSplit(true)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @Test
+ fun testBodySensorNonSplit() {
+ installPackage(APP_APK_PATH_LATEST)
+ testBodySensorPermissionSplit(false)
+ }
+
+ private fun testLocationPermissionSplit(expectSplit: Boolean) {
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ waitForWindowTransition = false
+ ) {
+ if (expectSplit) {
+ clickPermissionRequestSettingsLinkAndAllowAlways()
+ } else {
+ if (isWatch) {
+ clickPermissionRequestAllowForegroundButton()
+ } else {
+ doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() }
+ }
+ }
+ }
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, expectSplit)
+ }
+
+ private fun testBodySensorPermissionSplit(expectSplit: Boolean) {
+ assertAppHasPermission(android.Manifest.permission.BODY_SENSORS, false)
+ assertAppHasPermission(android.Manifest.permission.BODY_SENSORS_BACKGROUND, false)
+
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.BODY_SENSORS to true,
+ waitForWindowTransition = false
+ ) {
+ if (expectSplit) {
+ clickPermissionRequestSettingsLinkAndAllowAlways()
+ } else {
+ if (isWatch) {
+ clickPermissionRequestAllowForegroundButton()
+ } else {
+ doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() }
+ }
+ }
+ }
+
+ assertAppHasPermission(android.Manifest.permission.BODY_SENSORS_BACKGROUND, expectSplit)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionTapjackingTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTapjackingTest.kt
new file mode 100644
index 000000000..b81432369
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTapjackingTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.Point
+import android.os.Build
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+
+/** Tests permissions can't be tapjacked */
+@FlakyTest
+class PermissionTapjackingTest : BaseUsePermissionTest() {
+
+ @Before
+ fun installAppLatest() {
+ installPackage(APP_APK_PATH_WITH_OVERLAY)
+ }
+
+ @Test
+ fun testTapjackGrantDialog_fullOverlay() {
+ // PermissionController for television uses a floating window.
+ assumeFalse(isTv)
+
+ // Automotive split-screen multitasking uses multi-window mode
+ assumeFalse(isAutomotiveSplitscreen)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
+
+ val buttonCenter =
+ waitFindObject(By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)))
+ .visibleCenter
+
+ // Wait for overlay to hide the dialog
+ context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY).putExtra(EXTRA_FULL_OVERLAY, true))
+ waitFindObject(By.res("android.permissionui.cts.usepermission:id/overlay"))
+
+ tryClicking(buttonCenter)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+ @Test
+ fun testTapjackGrantDialog_partialOverlay() {
+ // PermissionController for television uses a floating window.
+ assumeFalse(isTv)
+
+ // Automotive split-screen multitasking uses multi-window mode
+ assumeFalse(isAutomotiveSplitscreen)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
+
+ val foregroundButtonCenter =
+ waitFindObject(By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)))
+ .visibleCenter
+ val oneTimeButton =
+ waitFindObjectOrNull(By.text(getPermissionControllerString(ALLOW_ONE_TIME_BUTTON_TEXT)))
+ // If one-time button is not available, fallback to deny button
+ val overlayButtonBounds =
+ oneTimeButton?.visibleBounds
+ ?: waitFindObject(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ .visibleBounds
+
+ // Wait for overlay to hide the dialog
+ context.sendBroadcast(
+ Intent(ACTION_SHOW_OVERLAY)
+ .putExtra(EXTRA_FULL_OVERLAY, false)
+ .putExtra(OVERLAY_LEFT, overlayButtonBounds.left)
+ .putExtra(OVERLAY_TOP, overlayButtonBounds.top)
+ .putExtra(OVERLAY_RIGHT, overlayButtonBounds.right)
+ .putExtra(OVERLAY_BOTTOM, overlayButtonBounds.bottom)
+ )
+ waitFindObject(By.res("android.permissionui.cts.usepermission:id/overlay"))
+
+ tryClicking(foregroundButtonCenter)
+ }
+
+ private fun tryClicking(buttonCenter: Point) {
+ try {
+ // Try to grant the permission, this should fail
+ SystemUtil.eventually(
+ {
+ if (
+ packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
+ PackageManager.PERMISSION_DENIED
+ ) {
+ uiDevice.click(buttonCenter.x, buttonCenter.y)
+ Thread.sleep(100)
+ }
+ assertAppHasPermission(ACCESS_FINE_LOCATION, true)
+ },
+ 10000
+ )
+ } catch (e: RuntimeException) {
+ // expected
+ }
+ // Permission should not be granted
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+
+ // Verify that clicking the dialog without the overlay still works
+ context.sendBroadcast(Intent(ACTION_HIDE_OVERLAY))
+ SystemUtil.eventually(
+ {
+ if (
+ packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
+ PackageManager.PERMISSION_DENIED
+ ) {
+ uiDevice.click(buttonCenter.x, buttonCenter.y)
+ Thread.sleep(100)
+ }
+ assertAppHasPermission(ACCESS_FINE_LOCATION, true)
+ },
+ 10000
+ )
+ }
+
+ companion object {
+ const val ACTION_SHOW_OVERLAY = "android.permissionui.cts.usepermission.ACTION_SHOW_OVERLAY"
+ const val ACTION_HIDE_OVERLAY = "android.permissionui.cts.usepermission.ACTION_HIDE_OVERLAY"
+
+ const val EXTRA_FULL_OVERLAY = "android.permissionui.cts.usepermission.extra.FULL_OVERLAY"
+
+ const val OVERLAY_LEFT = "android.permissionui.cts.usepermission.extra.OVERLAY_LEFT"
+ const val OVERLAY_TOP = "android.permissionui.cts.usepermission.extra.OVERLAY_TOP"
+ const val OVERLAY_RIGHT = "android.permissionui.cts.usepermission.extra.OVERLAY_RIGHT"
+ const val OVERLAY_BOTTOM = "android.permissionui.cts.usepermission.extra.OVERLAY_BOTTOM"
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest22.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest22.kt
new file mode 100755
index 000000000..b6d5887d6
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest22.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import androidx.test.filters.FlakyTest
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+
+/** Runtime permission behavior tests for apps targeting API 22. */
+@FlakyTest
+class PermissionTest22 : BaseUsePermissionTest() {
+
+ @Before
+ fun installApp22AndApprovePermissionReview() {
+ Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
+ installPackage(APP_APK_PATH_22)
+ approvePermissionReview()
+ }
+
+ @Test
+ fun testCompatDefault() {
+ // Legacy permission model appears granted
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, true)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, true)
+ assertAppHasCalendarAccess(true)
+ }
+
+ @Test
+ fun testCompatRevoked() {
+ // Revoke the permission
+ revokeAppPermissionsByUi(android.Manifest.permission.WRITE_CALENDAR, isLegacyApp = true)
+
+ // Legacy permission model appears granted
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, true)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, true)
+ // Read/write access should be ignored
+ assertAppHasCalendarAccess(false)
+ }
+
+ @Test
+ fun testNoRuntimePrompt() {
+ // Request the permission and do nothing
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ arrayOf(android.Manifest.permission.SEND_SMS),
+ emptyArray(),
+ waitForWindowTransition = false
+ ) {}
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest23.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest23.kt
new file mode 100644
index 000000000..4f06fb417
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest23.kt
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.content.pm.PackageManager
+import android.permission.cts.MtsIgnore
+import androidx.test.filters.FlakyTest
+import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assert
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+
+/** Runtime permission behavior tests for apps targeting API 23. */
+class PermissionTest23 : BaseUsePermissionTest() {
+ companion object {
+ private const val NON_EXISTENT_PERMISSION = "permission.does.not.exist"
+ private const val INVALID_PERMISSION = "$APP_PACKAGE_NAME.abadname"
+ }
+
+ @Before
+ fun installApp23() {
+ installPackage(APP_APK_PATH_23)
+ }
+
+ @Test
+ @FlakyTest
+ fun testDefault() {
+ // New permission model is denied by default
+ assertAppHasAllOrNoPermissions(false)
+ }
+
+ @Test
+ @FlakyTest
+ fun testGranted() {
+ grantAppPermissionsByUi(android.Manifest.permission.READ_CALENDAR)
+
+ // Read/write access should be allowed
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, true)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, true)
+ assertAppHasCalendarAccess(true)
+ }
+
+ @Test
+ @FlakyTest
+ fun testInteractiveGrant() {
+ // Start out without permission
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, false)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, false)
+ assertAppHasCalendarAccess(false)
+
+ // Go through normal grant flow
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.READ_CALENDAR to true,
+ android.Manifest.permission.WRITE_CALENDAR to true
+ ) {
+ clickPermissionRequestAllowButton()
+ }
+
+ // We should have permission now!
+ assertAppHasCalendarAccess(true)
+ }
+
+ @Test
+ @FlakyTest
+ fun testRuntimeGroupGrantSpecificity() {
+ // Start out without permission
+ assertAppHasPermission(android.Manifest.permission.READ_CONTACTS, false)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CONTACTS, false)
+
+ // Request only one permission from the 'contacts' permission group
+ // Expect the permission is granted
+ requestAppPermissionsAndAssertResult(android.Manifest.permission.WRITE_CONTACTS to true) {
+ clickPermissionRequestAllowButton()
+ }
+
+ // Make sure no undeclared as used permissions are granted
+ assertAppHasPermission(android.Manifest.permission.READ_CONTACTS, false)
+ }
+
+ @Test
+ @FlakyTest
+ fun testCancelledPermissionRequest() {
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.WRITE_CONTACTS, false)
+
+ // Request the permission and cancel the request
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(android.Manifest.permission.WRITE_CONTACTS to false) {
+ clickPermissionRequestDenyButton()
+ }
+ }
+
+ @Test
+ @FlakyTest
+ fun testRequestGrantedPermission() {
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.WRITE_CONTACTS, false)
+
+ // Request the permission and allow it
+ // Expect the permission is granted
+ requestAppPermissionsAndAssertResult(android.Manifest.permission.WRITE_CONTACTS to true) {
+ clickPermissionRequestAllowButton()
+ }
+
+ // Request the permission and do nothing
+ // Expect the permission is granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.WRITE_CONTACTS to true,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ @FlakyTest
+ fun testDenialWithPrejudice() {
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.WRITE_CONTACTS, false)
+
+ // Request the permission and deny it twice
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.WRITE_CONTACTS to false,
+ askTwice = true
+ ) {
+ clickPermissionRequestDenyButton()
+ denyPermissionRequestWithPrejudice()
+ }
+
+ // Request the permission and do nothing
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.WRITE_CONTACTS to false,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @FlakyTest
+ @MtsIgnore
+ @Test
+ fun testRevokeAffectsWholeGroup() {
+ // Grant the group
+ grantAppPermissionsByUi(android.Manifest.permission.READ_CALENDAR)
+
+ // Make sure we have the permissions
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, true)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, true)
+
+ // Revoke the group
+ revokeAppPermissionsByUi(android.Manifest.permission.READ_CALENDAR)
+
+ // Make sure we don't have the permissions
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, false)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, false)
+ }
+
+ @Test
+ @FlakyTest
+ fun testGrantPreviouslyRevokedWithPrejudiceShowsPrompt() {
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, false)
+
+ // Request the permission and deny it twice
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.READ_CALENDAR to false,
+ askTwice = true
+ ) {
+ clickPermissionRequestDenyButton()
+ denyPermissionRequestWithPrejudice()
+ }
+
+ // Clear the denial with prejudice
+ uiAutomation.grantRuntimePermission(
+ APP_PACKAGE_NAME,
+ android.Manifest.permission.READ_CALENDAR
+ )
+ revokeAppPermissionsByUi(android.Manifest.permission.READ_CALENDAR)
+
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, false)
+
+ // Request the permission and allow it
+ // Make sure the permission is granted
+ requestAppPermissionsAndAssertResult(android.Manifest.permission.READ_CALENDAR to true) {
+ clickPermissionRequestAllowButton()
+ }
+ }
+
+ @Test
+ @FlakyTest
+ fun testRequestNonRuntimePermission() {
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.BIND_PRINT_SERVICE, false)
+
+ // Request the permission and do nothing
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.BIND_PRINT_SERVICE to false,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ @FlakyTest
+ fun testRequestNonExistentPermission() {
+ // Make sure we don't have the permission
+ assertAppHasPermission(NON_EXISTENT_PERMISSION, false)
+
+ // Request the permission and do nothing
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ NON_EXISTENT_PERMISSION to false,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ @FlakyTest
+ fun testRequestPermissionFromTwoGroups() {
+ // Make sure we don't have the permissions
+ assertAppHasPermission(android.Manifest.permission.WRITE_CONTACTS, false)
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, false)
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, false)
+
+ // Request the permission and allow it
+ // Expect the permission are granted
+ val result =
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.WRITE_CONTACTS to true,
+ android.Manifest.permission.WRITE_CALENDAR to true
+ ) {
+ clickPermissionRequestAllowButton()
+ clickPermissionRequestAllowButton()
+ }
+
+ // In API < N_MR1 all permissions of a group are granted. I.e. the grant was "expanded"
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, true)
+ // Even the contacts group was expanded, the read-calendar permission is not in the
+ // manifest, hence not granted.
+ assertAppHasPermission(android.Manifest.permission.READ_CONTACTS, false)
+ }
+
+ @Test(timeout = 180000)
+ @FlakyTest
+ @MtsIgnore
+ fun testNoResidualPermissionsOnUninstall() {
+ Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
+ // Grant one permission via UI, and the rest via automation
+ grantAppPermissionsByUi(android.Manifest.permission.WRITE_CALENDAR)
+ grantRuntimePermissions(
+ android.Manifest.permission.WRITE_CONTACTS,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.CALL_PHONE,
+ android.Manifest.permission.RECORD_AUDIO,
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.BODY_SENSORS,
+ )
+ uninstallPackage(APP_PACKAGE_NAME)
+ installPackage(APP_APK_PATH_23)
+
+ // Make no permissions are granted after uninstalling and installing the app
+ assertAppHasAllOrNoPermissions(false)
+ }
+
+ @Test
+ @FlakyTest
+ fun testNullPermissionRequest() {
+ val permissions: Array<String?> = arrayOf(null)
+ val results: Array<Pair<String?, Boolean>> = arrayOf()
+ // Go through normal grant flow
+ requestAppPermissionsAndAssertResult(
+ permissions,
+ results,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ @FlakyTest
+ fun testNullAndRealPermission() {
+ // Make sure we don't have the permissions
+ assertAppHasPermission(android.Manifest.permission.WRITE_CONTACTS, false)
+ assertAppHasPermission(android.Manifest.permission.RECORD_AUDIO, false)
+
+ // Request the permission and allow it
+ // Expect the permission are granted
+ requestAppPermissionsAndAssertResult(
+ arrayOf(
+ android.Manifest.permission.WRITE_CONTACTS,
+ null,
+ android.Manifest.permission.RECORD_AUDIO,
+ null
+ ),
+ arrayOf(
+ android.Manifest.permission.WRITE_CONTACTS to true,
+ android.Manifest.permission.RECORD_AUDIO to true
+ )
+ ) {
+ clickPermissionRequestAllowForegroundButton()
+ clickPermissionRequestAllowButton()
+ }
+ }
+
+ @Test
+ @FlakyTest
+ fun testInvalidPermission() {
+ // Request the permission and allow it
+ // Expect the permission is not granted
+ requestAppPermissionsAndAssertResult(
+ INVALID_PERMISSION to false,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ @FlakyTest
+ fun testAskButtonSetsFlags() {
+ Assume.assumeFalse(
+ "other form factors might not support the ask button",
+ isTv || isAutomotive || isWatch
+ )
+
+ grantAppPermissionsByUi(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, true)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, true)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, true)
+
+ revokeAppPermissionsByUi(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ SystemUtil.runWithShellPermissionIdentity {
+ val perms =
+ listOf(
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ for (perm in perms) {
+ var flags = packageManager.getPermissionFlags(perm, APP_PACKAGE_NAME, context.user)
+ Assert.assertEquals(
+ "USER_SET should not be set for $perm",
+ 0,
+ flags and PackageManager.FLAG_PERMISSION_USER_SET
+ )
+ Assert.assertEquals(
+ "USER_FIXED should not be set for $perm",
+ 0,
+ flags and PackageManager.FLAG_PERMISSION_USER_FIXED
+ )
+ Assert.assertEquals(
+ "ONE_TIME should be set for $perm",
+ PackageManager.FLAG_PERMISSION_ONE_TIME,
+ flags and PackageManager.FLAG_PERMISSION_ONE_TIME
+ )
+ }
+ }
+ }
+
+ private fun denyPermissionRequestWithPrejudice() {
+ if (isTv || isWatch) {
+ clickPermissionRequestDontAskAgainButton()
+ } else {
+ clickPermissionRequestDenyAndDontAskAgainButton()
+ }
+ }
+
+ private fun assertAppHasAllOrNoPermissions(expectPermissions: Boolean) {
+ arrayOf(
+ android.Manifest.permission.SEND_SMS,
+ android.Manifest.permission.RECEIVE_SMS,
+ android.Manifest.permission.RECEIVE_WAP_PUSH,
+ android.Manifest.permission.RECEIVE_MMS,
+ android.Manifest.permission.READ_CALENDAR,
+ android.Manifest.permission.WRITE_CALENDAR,
+ android.Manifest.permission.WRITE_CONTACTS,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_CALL_LOG,
+ android.Manifest.permission.WRITE_CALL_LOG,
+ android.Manifest.permission.ADD_VOICEMAIL,
+ android.Manifest.permission.CALL_PHONE,
+ android.Manifest.permission.USE_SIP,
+ android.Manifest.permission.PROCESS_OUTGOING_CALLS,
+ android.Manifest.permission.RECORD_AUDIO,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.BODY_SENSORS,
+ android.Manifest.permission.READ_CELL_BROADCASTS,
+ // Split permissions
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ // Storage permissions
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+ )
+ .forEach { assertAppHasPermission(it, expectPermissions) }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest29.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest29.kt
new file mode 100644
index 000000000..3278596d4
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest29.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.content.ComponentName
+import android.content.Intent
+import android.permission.cts.MtsIgnore
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.filters.FlakyTest
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.eventually
+import org.junit.Assert
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+
+/** Runtime permission behavior tests for apps targeting API 29. */
+@FlakyTest
+class PermissionTest29 : BaseUsePermissionTest() {
+ @Before
+ fun assumeNotTv() {
+ assumeFalse(isTv)
+ }
+
+ @Before
+ fun installApp29() {
+ installPackage(APP_APK_PATH_29)
+ }
+
+ @Before
+ fun assertAppHasNoPermissions() {
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+ }
+
+ @Test
+ fun testRequestOnlyBackgroundNotPossible() {
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false,
+ waitForWindowTransition = false
+ ) {}
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+ }
+
+ @Test
+ fun testRequestBoth() {
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to true,
+ waitForWindowTransition = false
+ ) {
+ clickPermissionRequestSettingsLinkAndAllowAlways()
+ }
+ }
+
+ @Test
+ fun testRequestBothInSequence() {
+ // Step 1: request foreground only
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true
+ ) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+
+ // Step 2: request background only
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to true,
+ waitForWindowTransition = false
+ ) {
+ clickPermissionRequestSettingsLinkAndAllowAlways()
+ }
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, true)
+ }
+
+ @Test
+ fun testRequestBothButGrantInSequence() {
+ // Step 1: grant foreground only
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false
+ ) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ // Step 2: grant background
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to true,
+ waitForWindowTransition = false
+ ) {
+ clickPermissionRequestSettingsLinkAndAllowAlways()
+ }
+ }
+
+ @FlakyTest
+ @MtsIgnore
+ @Test
+ fun testDenyBackgroundWithPrejudice() {
+ // Step 1: deny the first time
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to false,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false
+ ) {
+ clickPermissionRequestDenyButton()
+ }
+
+ // Step 2: deny with prejudice
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to false,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false
+ ) {
+ clickPermissionRequestDenyAndDontAskAgainButton()
+ }
+
+ // Step 3: All further requests should be denied automatically
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to false,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @FlakyTest
+ @MtsIgnore
+ @Test
+ fun testGrantDialogToSettingsNoOp() {
+ // Step 1: Request both, go to settings, do nothing
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false,
+ waitForWindowTransition = false
+ ) {
+ openSettingsThenDoNothingThenLeave()
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+
+ doAndWaitForWindowTransition { clickPermissionRequestAllowForegroundButton() }
+ }
+
+ // Step 2: Upgrade foreground to background, go to settings, do nothing
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false,
+ waitForWindowTransition = false
+ ) {
+ openSettingsThenDoNothingThenLeave()
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, true)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+
+ doAndWaitForWindowTransition { clickPermissionRequestNoUpgradeAndDontAskAgainButton() }
+ }
+ }
+
+ private fun openSettingsThenDoNothingThenLeave() {
+ clickPermissionRequestSettingsLink()
+ eventually {
+ pressBack()
+ if (isAutomotive) {
+ waitFindObject(By.textContains("Allow in settings."), 100)
+ } else if (isWatch) {
+ waitForIdleLong()
+ findAccessibilityNodeInfosByTextForSurfaceView(
+ uiAutomation.rootInActiveWindow,
+ "Allow in settings")
+ } else {
+ waitFindObject(By.res("com.android.permissioncontroller:id/grant_dialog"), 100)
+ }
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun testGrantDialogToSettingsDowngrade() {
+ // Request upgrade, downgrade permission to denied in settings
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.ACCESS_FINE_LOCATION to true,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION to false
+ ) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ requestAppPermissions(
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ waitForWindowTransition = false
+ ) {
+ clickPermissionRequestSettingsLinkAndDeny()
+ pressBack()
+ }
+
+ assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = [313909156])
+ fun testAppCanOnlyShowOneDialog() {
+ uninstallPackage(APP_PACKAGE_NAME, requireSuccess = true)
+ installPackage(APP_APK_PATH_TWO_PERM_REQUESTS)
+ doAndWaitForWindowTransition {
+ val intent =
+ Intent(Intent.ACTION_MAIN)
+ .setComponent(ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.Activity1"))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ context.startActivity(intent)
+ }
+ waitFindObject(
+ By.textContains("contacts").pkg(packageManager.permissionControllerPackageName)
+ )
+ var didNotFindPhone = false
+ try {
+ waitFindObject(
+ By.textContains("phone calls").pkg(packageManager.permissionControllerPackageName),
+ 3000L
+ )
+ } catch (expected: Exception) {
+ didNotFindPhone = true
+ }
+ Assert.assertTrue("Found phone permission dialog", didNotFindPhone)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30.kt
new file mode 100644
index 000000000..25bdab298
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import androidx.test.filters.FlakyTest
+import androidx.test.uiautomator.By
+import org.junit.Assert.assertNull
+import org.junit.Test
+
+/** Runtime permission behavior apps targeting API 30 */
+@FlakyTest
+class PermissionTest30 : BaseUsePermissionTest() {
+
+ @Test
+ fun testCantRequestFgAndBgAtOnce() {
+ // TODO(b/280542662): This delay is a temporary mitigation for an intermittent failure
+ Thread.sleep(500)
+ installPackage(APP_APK_PATH_30_WITH_BACKGROUND)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to false,
+ ACCESS_BACKGROUND_LOCATION to false,
+ waitForWindowTransition = false
+ ) {
+ // Do nothing, should be automatically denied
+ }
+ }
+
+ @Test
+ fun testRequestBothInSequence() {
+ installPackage(APP_APK_PATH_30_WITH_BACKGROUND)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to true) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ requestAppPermissionsAndAssertResult(
+ ACCESS_BACKGROUND_LOCATION to true,
+ waitForWindowTransition = false
+ ) {
+ clickAllowAlwaysInSettings()
+ pressBack()
+ }
+ }
+
+ @Test
+ fun testRequestFgLocationAndNoAccuracyOptions() {
+ installPackage(APP_APK_PATH_30)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(
+ ACCESS_FINE_LOCATION to false,
+ ACCESS_COARSE_LOCATION to false
+ ) {
+ // Verify there's no location accuracy options
+ val locationAccuracyOptions =
+ waitFindObjectOrNull(
+ By.res("com.android.permissioncontroller:id/permission_location_accuracy"),
+ 1000L
+ )
+ assertNull(
+ "For apps targetSDK < 31, location permission dialog shouldn't show " +
+ "accuracy options. Please update the system with " +
+ "the latest (at least Oct, 2021) mainline modules.",
+ locationAccuracyOptions
+ )
+ // Close dialog
+ clickPermissionRequestDenyButton()
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30WithBluetooth.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30WithBluetooth.kt
new file mode 100644
index 000000000..61b8fd5d9
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionTest30WithBluetooth.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.Manifest.permission.BLUETOOTH_SCAN
+import android.app.AppOpsManager
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.test_utils.EnableBluetoothRule
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT
+import android.location.LocationManager
+import android.os.Build
+import android.os.Process
+import android.os.UserHandle
+import android.util.Log
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.harrier.annotations.RequireNotAutomotive
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import junit.framework.AssertionFailedError
+import org.junit.After
+import org.junit.Assert.assertNotEquals
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val LOG_TAG = "PermissionTest30WithBluetooth"
+
+/** Runtime Bluetooth-permission behavior of apps targeting API 30 */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+@RunWith(BedsteadJUnit4::class)
+@FlakyTest
+class PermissionTest30WithBluetooth : BaseUsePermissionTest() {
+ companion object {
+ @get:ClassRule @JvmStatic val enableBluetooth = EnableBluetoothRule(true)
+ @ClassRule @Rule @JvmField val sDeviceState = DeviceState()
+ }
+
+ private val TEST_APP_AUTHORITY =
+ "android.permissionui.cts.usepermission.AccessBluetoothOnCommand"
+ private val TEST_APP_PKG = "android.permissionui.cts.usepermission"
+ private lateinit var bluetoothAdapter: BluetoothAdapter
+ private val locationManager = context.getSystemService(LocationManager::class.java)!!
+ private var locationWasEnabled: Boolean? = null
+
+ private enum class BluetoothScanResult {
+ UNKNOWN,
+ ERROR,
+ EXCEPTION,
+ EMPTY,
+ FILTERED,
+ FULL
+ }
+
+ @Before
+ fun installApp() {
+ installPackage(APP_APK_PATH_30_WITH_BLUETOOTH)
+ }
+
+ private fun reinstallApp() {
+ installPackage(APP_APK_PATH_30_WITH_BLUETOOTH, reinstall = true)
+ }
+
+ @Before
+ fun enableLocation() {
+ val userHandle: UserHandle = Process.myUserHandle()
+ locationWasEnabled = locationManager.isLocationEnabledForUser(userHandle)
+ if (locationWasEnabled == false) {
+ runWithShellPermissionIdentity {
+ locationManager.setLocationEnabledForUser(true, userHandle)
+ }
+ }
+ }
+
+ @After
+ fun disableLocation() {
+ val userHandle: UserHandle = Process.myUserHandle()
+
+ if (locationWasEnabled == false) {
+ runWithShellPermissionIdentity {
+ locationManager.setLocationEnabledForUser(false, userHandle)
+ }
+ }
+ }
+
+ // TODO:(b/220030722) Remove verbose logging (after test is stabilized)
+ // TODO:(b/317442167) Fix permission scroll on auto portrait
+ @Test
+ @RequireNotAutomotive(reason = "Permission scroll is not working on auto portrait")
+ fun testGivenBluetoothIsDeniedWhenScanIsAttemptedThenThenGetEmptyScanResult() {
+ assumeTrue(supportsBluetoothLe())
+
+ assertTrue(
+ "Please enable location to run this test. Bluetooth scanning " +
+ "requires location to be enabled.",
+ locationManager.isLocationEnabled()
+ )
+ assertBluetoothRevokedCompatState(revoked = false)
+
+ Log.v(
+ LOG_TAG,
+ "Testing for: Given {BLUETOOTH_SCAN, !BLUETOOTH_SCAN.COMPAT_REVOKE, " +
+ "!ACCESS_*_LOCATION}, expect EMPTY"
+ )
+ assertEquals(BluetoothScanResult.EMPTY, scanForBluetoothDevices())
+
+ Log.v(
+ LOG_TAG,
+ "Testing for: Given {BLUETOOTH_SCAN, !BLUETOOTH_SCAN.COMPAT_REVOKE, " +
+ "ACCESS_*_LOCATION}, expect FULL"
+ )
+ uiAutomation.grantRuntimePermission(TEST_APP_PKG, ACCESS_FINE_LOCATION)
+ uiAutomation.grantRuntimePermission(TEST_APP_PKG, ACCESS_BACKGROUND_LOCATION)
+ setAppOp(context.packageName, AppOpsManager.OPSTR_FINE_LOCATION, AppOpsManager.MODE_ALLOWED)
+ assertEquals(BluetoothScanResult.FULL, scanForBluetoothDevices())
+
+ Log.v(
+ LOG_TAG,
+ "Testing for: Given {BLUETOOTH_SCAN, BLUETOOTH_SCAN.COMPAT_REVOKE, " +
+ "ACCESS_*_LOCATION}, expect ERROR"
+ )
+ revokeAppPermissionsByUi(BLUETOOTH_SCAN, isLegacyApp = true)
+ assertBluetoothRevokedCompatState(revoked = true)
+ val res = scanForBluetoothDevices()
+ if (res != BluetoothScanResult.ERROR && res != BluetoothScanResult.EMPTY) {
+ throw AssertionFailedError("Expected to be EMPTY or ERROR, but was $res")
+ }
+ }
+
+ private fun setAppOp(packageName: String, appOp: String, appOpMode: Int) {
+ runWithShellPermissionIdentity {
+ context
+ .getSystemService(AppOpsManager::class.java)!!
+ .setUidMode(appOp, packageManager.getPackageUid(packageName, 0), appOpMode)
+ }
+ }
+
+ // TODO:(b/317442167) Fix permission scroll on auto portrait
+ @Test
+ @RequireNotAutomotive(reason = "Permission scroll is not working on auto portrait")
+ fun testRevokedCompatPersistsOnReinstall() {
+ assertBluetoothRevokedCompatState(revoked = false)
+ revokeAppPermissionsByUi(BLUETOOTH_SCAN, isLegacyApp = true)
+ assertBluetoothRevokedCompatState(revoked = true)
+ reinstallApp()
+ assertBluetoothRevokedCompatState(revoked = true)
+ installApp()
+ assertBluetoothRevokedCompatState(revoked = true)
+ }
+
+ private fun assertBluetoothRevokedCompatState(revoked: Boolean = true) {
+ runWithShellPermissionIdentity {
+ val flag =
+ context.packageManager.getPermissionFlags(
+ BLUETOOTH_SCAN,
+ TEST_APP_PKG,
+ Process.myUserHandle()
+ ) and FLAG_PERMISSION_REVOKED_COMPAT
+ if (revoked) {
+ assertNotEquals(0, flag)
+ } else {
+ assertEquals(0, flag)
+ }
+ }
+ }
+
+ private fun scanForBluetoothDevices(): BluetoothScanResult {
+ val resolver = InstrumentationRegistry.getTargetContext().getContentResolver()
+ val result = resolver.call(TEST_APP_AUTHORITY, "", null, null)
+ return BluetoothScanResult.values()[result!!.getInt(Intent.EXTRA_INDEX)]
+ }
+
+ private fun supportsBluetoothLe(): Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionUpgradeTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionUpgradeTest.kt
new file mode 100644
index 000000000..3948e984f
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionUpgradeTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import androidx.test.filters.FlakyTest
+import org.junit.Assume
+import org.junit.Test
+
+/** Runtime permission behavior tests for upgrading apps. */
+@FlakyTest
+class PermissionUpgradeTest : BaseUsePermissionTest() {
+
+ @Test
+ fun testUpgradeKeepsPermissions() {
+ Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
+ installPackage(APP_APK_PATH_22)
+
+ approvePermissionReview()
+
+ assertAllPermissionsGrantedByDefault()
+
+ installPackage(APP_APK_PATH_23, reinstall = true, skipClearLowSdkDialog = true)
+
+ assertAllPermissionsGrantedOnUpgrade()
+ }
+
+ private fun assertAllPermissionsGrantedByDefault() {
+ arrayOf(
+ android.Manifest.permission.SEND_SMS,
+ android.Manifest.permission.RECEIVE_SMS,
+ // The APK does not request READ_CONTACTS because of other tests
+ android.Manifest.permission.WRITE_CONTACTS,
+ android.Manifest.permission.READ_CALENDAR,
+ android.Manifest.permission.WRITE_CALENDAR,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.RECEIVE_WAP_PUSH,
+ android.Manifest.permission.RECEIVE_MMS,
+ "android.permission.READ_CELL_BROADCASTS",
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.CALL_PHONE,
+ android.Manifest.permission.READ_CALL_LOG,
+ android.Manifest.permission.WRITE_CALL_LOG,
+ android.Manifest.permission.ADD_VOICEMAIL,
+ android.Manifest.permission.USE_SIP,
+ android.Manifest.permission.PROCESS_OUTGOING_CALLS,
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.BODY_SENSORS,
+ // Split permissions
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ )
+ .forEach { assertAppHasPermission(it, true) }
+ }
+
+ private fun assertAllPermissionsGrantedOnUpgrade() {
+ assertAppHasAllOrNoPermissions(true)
+ }
+
+ @Test
+ fun testNoDowngradePermissionModel() {
+ installPackage(APP_APK_PATH_23, skipClearLowSdkDialog = true)
+ installPackage(APP_APK_PATH_22, reinstall = true, expectSuccess = false)
+ }
+
+ @Test
+ fun testRevokePropagatedOnUpgradeOldToNewModel() {
+ Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled())
+
+ installPackage(APP_APK_PATH_22)
+
+ approvePermissionReview()
+
+ // Revoke a permission
+ revokeAppPermissionsByUi(android.Manifest.permission.WRITE_CALENDAR, isLegacyApp = true)
+
+ installPackage(APP_APK_PATH_23, reinstall = true, skipClearLowSdkDialog = true)
+
+ assertAppHasPermission(android.Manifest.permission.WRITE_CALENDAR, false)
+ }
+
+ @Test
+ fun testRevokePropagatedOnUpgradeNewToNewModel() {
+ installPackage(APP_APK_PATH_23)
+
+ // Make sure we don't have the permission
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, false)
+ assertAppHasPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE, false)
+
+ // Request the permission and allow it
+ // Make sure the permission is granted
+ requestAppPermissionsAndAssertResult(
+ android.Manifest.permission.READ_CALENDAR to true,
+ ) {
+ clickPermissionRequestAllowButton()
+ }
+
+ installPackage(APP_APK_PATH_23, reinstall = true, skipClearLowSdkDialog = true)
+
+ // Make sure the permission is still granted after the upgrade
+ assertAppHasPermission(android.Manifest.permission.READ_CALENDAR, true)
+ // Also make sure one of the not granted permissions is still not granted
+ assertAppHasPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE, false)
+ }
+
+ private fun assertAppHasAllOrNoPermissions(expectPermissions: Boolean) {
+ arrayOf(
+ android.Manifest.permission.SEND_SMS,
+ android.Manifest.permission.RECEIVE_SMS,
+ android.Manifest.permission.RECEIVE_WAP_PUSH,
+ android.Manifest.permission.RECEIVE_MMS,
+ android.Manifest.permission.READ_CALENDAR,
+ android.Manifest.permission.WRITE_CALENDAR,
+ android.Manifest.permission.WRITE_CONTACTS,
+ android.Manifest.permission.READ_SMS,
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.READ_CALL_LOG,
+ android.Manifest.permission.WRITE_CALL_LOG,
+ android.Manifest.permission.ADD_VOICEMAIL,
+ android.Manifest.permission.CALL_PHONE,
+ android.Manifest.permission.USE_SIP,
+ android.Manifest.permission.PROCESS_OUTGOING_CALLS,
+ android.Manifest.permission.RECORD_AUDIO,
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ android.Manifest.permission.CAMERA,
+ android.Manifest.permission.BODY_SENSORS,
+ android.Manifest.permission.READ_CELL_BROADCASTS,
+ // Split permissions
+ android.Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+ // Storage permissions
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+ )
+ .forEach { assertAppHasPermission(it, expectPermissions) }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PermissionUsageInfoTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PermissionUsageInfoTest.kt
new file mode 100644
index 000000000..80e3dfaed
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PermissionUsageInfoTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.content.Intent
+import androidx.test.filters.FlakyTest
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import org.junit.Assume.assumeFalse
+import org.junit.Before
+import org.junit.Test
+
+/** Tests permission usage info action. */
+@FlakyTest
+class PermissionUsageInfoTest : BaseUsePermissionTest() {
+ @Before
+ fun assumeHandheld() {
+ assumeFalse(isAutomotive)
+ assumeFalse(isTv)
+ assumeFalse(isWatch)
+ }
+
+ @Before
+ fun installApp() {
+ installPackage(APP_APK_PATH_LATEST)
+ }
+
+ @Test
+ fun testPermissionUsageInfo() {
+ doAndWaitForWindowTransition {
+ runWithShellPermissionIdentity {
+ context.startActivity(
+ Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply {
+ putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ )
+ }
+ }
+ click(By.res("com.android.permissioncontroller:id/icon"))
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerPermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerPermissionTest.kt
new file mode 100644
index 000000000..9f76479e3
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerPermissionTest.kt
@@ -0,0 +1,523 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_MEDIA_LOCATION
+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.app.UiAutomation.ROTATION_FREEZE_0
+import android.app.UiAutomation.ROTATION_FREEZE_270
+import android.app.UiAutomation.ROTATION_UNFREEZE
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME
+import android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED
+import android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET
+import android.net.Uri
+import android.os.Build
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_PRIVACY
+import androidx.test.filters.FlakyTest
+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
+import org.junit.AfterClass
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@FlakyTest
+class PhotoPickerPermissionTest : BaseUsePermissionTest() {
+
+ companion object {
+ private var photoUri: Uri? = null
+ private var videoUri: Uri? = null
+ private var oldEnableState: Boolean = true
+
+ @BeforeClass
+ @JvmStatic
+ fun enablePickerAndAddMedia() {
+ // Initialize media provider package name
+ PhotoPickerUtils.getMediaProviderPkgName(context)
+ oldEnableState = isPhotoPickerPermissionPromptEnabled()
+ runWithShellPermissionIdentity {
+ if (!oldEnableState) {
+ DeviceConfig.setProperty(
+ NAMESPACE_PRIVACY,
+ PICKER_ENABLED_SETTING,
+ true.toString(),
+ false
+ )
+ }
+ photoUri = PhotoPickerUtils.createImage(context)
+ videoUri = PhotoPickerUtils.createVideo(context)
+ }
+ }
+
+ @AfterClass
+ @JvmStatic
+ fun resetPickerAndRemoveMedia() {
+ if (!oldEnableState) {
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(
+ NAMESPACE_PRIVACY,
+ PICKER_ENABLED_SETTING,
+ false.toString(),
+ false
+ )
+ }
+ }
+
+ PhotoPickerUtils.deleteMedia(context, photoUri)
+ PhotoPickerUtils.deleteMedia(context, videoUri)
+ }
+ }
+
+ @Before
+ fun assumeEnabled() {
+ assumeTrue(isPhotoPickerPermissionPromptEnabled())
+ }
+
+ @Test
+ fun testAppWithoutStoragePermsDoesntHaveUserSelectedAdded() {
+ installPackage(APP_APK_PATH_LATEST_NONE)
+ runWithShellPermissionIdentity {
+ val packageInfo =
+ packageManager.getPackageInfo(APP_PACKAGE_NAME, PackageManager.GET_PERMISSIONS)
+ assertNotNull(packageInfo)
+ val permissions = packageInfo.requestedPermissions?.toList() ?: emptyList<String>()
+ assertFalse(
+ "Expected app to not request READ_MEDIA_VISUAL_USER_SELECTED",
+ permissions.contains(READ_MEDIA_VISUAL_USER_SELECTED)
+ )
+ }
+ }
+
+ @Test
+ fun testAppWithStoragePermsHasUserSelectedAdded() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ runWithShellPermissionIdentity {
+ val packageInfo =
+ packageManager.getPackageInfo(APP_PACKAGE_NAME, PackageManager.GET_PERMISSIONS)
+ assertNotNull(packageInfo)
+ val permissions = packageInfo.requestedPermissions?.toList() ?: emptyList<String>()
+ assertTrue(
+ "Expected app to request READ_MEDIA_VISUAL_USER_SELECTED",
+ permissions.contains(READ_MEDIA_VISUAL_USER_SELECTED)
+ )
+ }
+ }
+
+ @Test
+ fun testAppWithUserSelectedPermShowsSelectOption() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ requestAppPermissions(READ_MEDIA_IMAGES) {
+ assertNotNull(waitFindObjectOrNull(By.res(SELECT_BUTTON)))
+ click(By.res(DENY_BUTTON))
+ }
+ }
+
+ @Test
+ fun testNoPhotoSelectionTreatedAsCancel() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ requestAppPermissionsAndAssertResult(
+ arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VISUAL_USER_SELECTED),
+ arrayOf(READ_MEDIA_IMAGES to false, READ_MEDIA_VISUAL_USER_SELECTED to false),
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ findImageOrVideo(expected = true)
+ uiDevice.pressBack()
+ }
+ assertPermissionFlags(READ_MEDIA_IMAGES, FLAG_PERMISSION_USER_SET to false)
+ assertPermissionFlags(READ_MEDIA_VISUAL_USER_SELECTED, FLAG_PERMISSION_USER_SET to false)
+ }
+
+ @Test
+ fun testImplicitUserSelectHasOneTimeGrantsWithoutAppOp() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ requestAppPermissionsAndAssertResult(
+ arrayOf(READ_MEDIA_IMAGES),
+ arrayOf(READ_MEDIA_IMAGES to true),
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ clickAllow()
+ }
+ eventually {
+ // USER_SELECTED should be granted, but not returned in the result
+ assertAppHasPermission(READ_MEDIA_VISUAL_USER_SELECTED, expectPermission = true)
+ assertAppHasPermission(READ_MEDIA_VIDEO, expectPermission = true)
+ assertPermissionFlags(
+ READ_MEDIA_IMAGES,
+ FLAG_PERMISSION_ONE_TIME to true,
+ FLAG_PERMISSION_REVOKED_COMPAT to true
+ )
+ assertPermissionFlags(
+ READ_MEDIA_VIDEO,
+ FLAG_PERMISSION_ONE_TIME to true,
+ FLAG_PERMISSION_REVOKED_COMPAT to true
+ )
+ assertPermissionFlags(
+ READ_MEDIA_VISUAL_USER_SELECTED,
+ FLAG_PERMISSION_ONE_TIME to false,
+ FLAG_PERMISSION_REVOKED_COMPAT to false
+ )
+ }
+ }
+
+ @Test
+ fun testImplicitShowsMorePhotosOnceSet() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ eventually {
+ uiAutomation.grantRuntimePermission(APP_PACKAGE_NAME, READ_MEDIA_VISUAL_USER_SELECTED)
+ assertAppHasPermission(READ_MEDIA_VISUAL_USER_SELECTED, true)
+ }
+
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ waitFindObject(By.res(DONT_SELECT_MORE_BUTTON))
+ uiDevice.pressBack()
+ }
+ }
+
+ @Test
+ fun testNonImplicitDoesntGrantOtherPermsWhenUserSelected() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VISUAL_USER_SELECTED),
+ arrayOf(READ_MEDIA_IMAGES to false, READ_MEDIA_VISUAL_USER_SELECTED to true),
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ clickAllow()
+ }
+
+ assertPermissionFlags(READ_MEDIA_IMAGES, FLAG_PERMISSION_USER_SET to true)
+ assertPermissionFlags(READ_MEDIA_VISUAL_USER_SELECTED, FLAG_PERMISSION_USER_SET to true)
+ }
+
+ @Test
+ fun testNonImplicitAutomaticallyShowsPickerWhenUserFixed() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ doAndWaitForWindowTransition { clickAllow() }
+ }
+
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ doAndWaitForWindowTransition { clickAllow() }
+ }
+
+ assertPermissionFlags(READ_MEDIA_VISUAL_USER_SELECTED, FLAG_PERMISSION_USER_FIXED to true)
+
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ findImageOrVideo(expected = true)
+ uiDevice.pressBack()
+ }
+ }
+
+ @Test
+ fun testRequestedPermsFilterMediaType() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ findImageOrVideo(expected = true)
+ findVideo(expected = false)
+ uiDevice.pressBack()
+ }
+
+ requestAppPermissions(READ_MEDIA_VIDEO, waitForWindowTransition = false) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ findVideo(expected = true)
+ uiDevice.pressBack()
+ }
+ }
+
+ @Test
+ fun testGrantAllPhotosStateSameForImplicitAndNot() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ requestAppPermissionsAndAssertResult(
+ arrayOf(READ_MEDIA_IMAGES),
+ arrayOf(READ_MEDIA_IMAGES to true)
+ ) {
+ click(By.res(ALLOW_ALL_BUTTON))
+ }
+
+ eventually {
+ assertAppHasPermission(READ_MEDIA_VISUAL_USER_SELECTED, expectPermission = true)
+ }
+
+ uninstallPackage(APP_PACKAGE_NAME)
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VISUAL_USER_SELECTED),
+ arrayOf(READ_MEDIA_IMAGES to true, READ_MEDIA_VISUAL_USER_SELECTED to true)
+ ) {
+ click(By.res(ALLOW_ALL_BUTTON))
+ }
+ }
+
+ @Test
+ fun testGrantAllPhotosInSettings() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ click(By.res(ALLOW_RADIO_BUTTON))
+
+ eventually {
+ assertAppHasPermission(READ_MEDIA_IMAGES, expectPermission = true)
+ assertAppHasPermission(READ_MEDIA_VIDEO, expectPermission = true)
+ assertAppHasPermission(ACCESS_MEDIA_LOCATION, expectPermission = true)
+ assertAppHasPermission(READ_MEDIA_VISUAL_USER_SELECTED, expectPermission = true)
+ }
+ }
+
+ @Test
+ fun testSelectPhotosInSettingsImplicit() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ click(By.res(SELECT_RADIO_BUTTON))
+
+ eventually {
+ assertAppHasPermission(READ_MEDIA_IMAGES, expectPermission = false)
+ assertAppHasPermission(READ_MEDIA_VIDEO, expectPermission = false)
+ assertAppHasPermission(ACCESS_MEDIA_LOCATION, expectPermission = false)
+ assertAppHasPermission(READ_MEDIA_VISUAL_USER_SELECTED, expectPermission = true)
+ }
+ }
+
+ @Test
+ fun testSelectPhotosInSettingsExplicit() {
+ installPackage(APP_APK_PATH_LATEST)
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ click(By.res(SELECT_RADIO_BUTTON))
+
+ eventually {
+ assertAppHasPermission(READ_MEDIA_IMAGES, expectPermission = false)
+ assertAppHasPermission(READ_MEDIA_VIDEO, expectPermission = false)
+ assertAppHasPermission(ACCESS_MEDIA_LOCATION, expectPermission = true)
+ assertAppHasPermission(READ_MEDIA_VISUAL_USER_SELECTED, expectPermission = true)
+ }
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun testPre33AppDoesntShowSelect() {
+ installPackage(APP_APK_PATH_30)
+ runWithShellPermissionIdentity {
+ val requestedPerms =
+ packageManager
+ .getPackageInfo(APP_PACKAGE_NAME, PackageManager.GET_PERMISSIONS)
+ .requestedPermissions!!
+ .toList()
+ assertTrue(
+ "Expected package to have USER_SELECTED",
+ requestedPerms.contains(READ_MEDIA_VISUAL_USER_SELECTED)
+ )
+ }
+
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ findView(By.res(SELECT_BUTTON), expected = false)
+ pressBack()
+ }
+
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ findView(By.res(SELECT_RADIO_BUTTON), expected = false)
+ }
+
+ @Test
+ fun test33AppWithImplicitUserSelectDoesntShowSelect() {
+ installPackage(APP_APK_PATH_STORAGE_33)
+
+ runWithShellPermissionIdentity {
+ val requestedPerms =
+ packageManager
+ .getPackageInfo(APP_PACKAGE_NAME, PackageManager.GET_PERMISSIONS)
+ .requestedPermissions!!
+ .toList()
+ assertTrue(
+ "Expected package to have USER_SELECTED",
+ requestedPerms.contains(READ_MEDIA_VISUAL_USER_SELECTED)
+ )
+ }
+
+ requestAppPermissions(READ_MEDIA_IMAGES, waitForWindowTransition = false) {
+ findView(By.res(SELECT_BUTTON), expected = false)
+ pressBack()
+ }
+
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ findView(By.res(SELECT_RADIO_BUTTON), expected = false)
+ }
+
+ @Test
+ fun testAppCantRequestOnlyPartialStoragePerms() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_VISUAL_USER_SELECTED to false,
+ waitForWindowTransition = false
+ ) {}
+ uninstallPackage(APP_PACKAGE_NAME)
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_VISUAL_USER_SELECTED to false,
+ ACCESS_MEDIA_LOCATION to false,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ fun testImplicitAppCanExpandAccessMediaLocation() {
+ installPackage(APP_APK_PATH_IMPLICIT_USER_SELECT_STORAGE)
+ requestAppPermissions(ACCESS_MEDIA_LOCATION) { click(By.res(ALLOW_ALL_BUTTON)) }
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_IMAGES to true,
+ READ_MEDIA_VIDEO to true,
+ waitForWindowTransition = false
+ ) {}
+ }
+
+ @Test
+ fun testExplicitAppCannotExpandAccessMediaLocation() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_IMAGES to false,
+ ACCESS_MEDIA_LOCATION to true,
+ READ_MEDIA_VISUAL_USER_SELECTED to true,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ clickAllow()
+ }
+ requestAppPermissions(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO) {
+ click(By.res(ALLOW_ALL_BUTTON))
+ }
+ }
+
+ @Test
+ fun testExplicitAppCannotRequestOnlyPartialAccess() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ ACCESS_MEDIA_LOCATION to false,
+ READ_MEDIA_VISUAL_USER_SELECTED to false,
+ waitForWindowTransition = false
+ ) {
+ findView(By.res(SELECT_BUTTON), expected = false)
+ }
+ }
+
+ @Test
+ fun testMorePhotosDialogShowsAfterClickingSelect() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_IMAGES to false,
+ ACCESS_MEDIA_LOCATION to true,
+ READ_MEDIA_VISUAL_USER_SELECTED to true,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ doAndWaitForWindowTransition { clickAllow() }
+ }
+
+ requestAppPermissions(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO) {
+ findView(By.res(DONT_SELECT_MORE_BUTTON), expected = true)
+ click(By.res(ALLOW_ALL_BUTTON))
+ }
+ }
+
+ @Test
+ fun testAMLNotGrantedIfNotRequested() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_IMAGES to false,
+ READ_MEDIA_VISUAL_USER_SELECTED to true,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ clickImageOrVideo()
+ doAndWaitForWindowTransition { clickAllow() }
+ }
+ assertAppHasPermission(ACCESS_MEDIA_LOCATION, false)
+ }
+
+ @Test
+ fun testDismissAfterActivityRecreatedWithPickerOpen() {
+ installPackage(APP_APK_PATH_LATEST)
+ requestAppPermissionsAndAssertResult(
+ READ_MEDIA_IMAGES to false,
+ READ_MEDIA_VISUAL_USER_SELECTED to true,
+ waitForWindowTransition = false
+ ) {
+ doAndWaitForWindowTransition { click(By.res(SELECT_BUTTON)) }
+ try {
+ doAndWaitForWindowTransition { uiAutomation.setRotation(ROTATION_FREEZE_270) }
+ doAndWaitForWindowTransition { uiAutomation.setRotation(ROTATION_FREEZE_0) }
+ clickImageOrVideo()
+ doAndWaitForWindowTransition { clickAllow() }
+ } finally {
+ uiAutomation.setRotation(ROTATION_UNFREEZE)
+ }
+ }
+ }
+
+ @Test
+ fun testCanSelectPhotosInSettings() {
+ installPackage(APP_APK_PATH_LATEST)
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ click(By.res(SELECT_RADIO_BUTTON))
+ doAndWaitForWindowTransition { click(By.res(EDIT_PHOTOS_BUTTON)) }
+ clickImageOrVideo()
+ clickAllow()
+ }
+
+ @Test
+ fun testEditButtonNotShownInSettingsWhenNoPhotosRequested() {
+ installPackage(APP_APK_PATH_LATEST)
+ navigateToIndividualPermissionSetting(READ_MEDIA_IMAGES)
+ UiAutomatorUtils2.waitUntilObjectGone(By.res(EDIT_PHOTOS_BUTTON))
+ }
+
+ private fun clickImageOrVideo() {
+ click(By.res(PhotoPickerUtils.getImageOrVideoResId(context)))
+ }
+
+ private fun clickAllow() {
+ click(By.res(PhotoPickerUtils.getAllowId(context)))
+ }
+
+ private fun findImageOrVideo(expected: Boolean) {
+ findView(By.res(PhotoPickerUtils.getImageOrVideoResId(context)), expected)
+ }
+
+ private fun findVideo(expected: Boolean) {
+ findView(By.res(PhotoPickerUtils.getVideoResId(context)), expected)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerUtils.kt b/tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerUtils.kt
new file mode 100644
index 000000000..db6c412db
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/PhotoPickerUtils.kt
@@ -0,0 +1,128 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.FileUtils
+import android.provider.MediaStore
+import android.provider.cts.media.MediaProviderTestUtils
+import android.provider.cts.media.MediaStoreUtils
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import java.io.IOException
+
+object PhotoPickerUtils {
+ private const val DISPLAY_NAME_PREFIX = "ctsPermissionPhotoPicker"
+ private const val VIDEO_ICON_ID = ":id/icon_video"
+ private const val IMAGE_CHECK_BOX_ID = ":id/icon_check"
+ private const val ALLOW_ID = ":id/button_add"
+ private var mediaProviderPkgName: String? = null
+
+ fun getImageOrVideoResId(context: Context): String {
+ return "${getMediaProviderPkgName(context)!!}$IMAGE_CHECK_BOX_ID"
+ }
+
+ fun getVideoResId(context: Context): String {
+ return "${getMediaProviderPkgName(context)!!}$VIDEO_ICON_ID"
+ }
+
+ fun getAllowId(context: Context): String {
+ return "${getMediaProviderPkgName(context)!!}$ALLOW_ID"
+ }
+
+ fun getMediaProviderPkgName(context: Context): String? {
+ return mediaProviderPkgName
+ ?: callWithShellPermissionIdentity {
+ val pkgs = context.packageManager.getInstalledPackages(PackageManager.GET_PROVIDERS)
+ for (pkg in pkgs) {
+ pkg.providers?.let { providerInfos ->
+ for (providerInfo in providerInfos) {
+ if (providerInfo.authority == "media") {
+ mediaProviderPkgName = pkg.packageName
+ return@callWithShellPermissionIdentity mediaProviderPkgName
+ }
+ }
+ }
+ }
+ null
+ }
+ }
+
+ @Throws(java.lang.Exception::class)
+ fun createImage(context: Context): Uri {
+ return getPermissionAndStageMedia(
+ context,
+ R.raw.lg_g4_iso_800_jpg,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ "image/jpeg"
+ )
+ .first
+ }
+
+ @Throws(java.lang.Exception::class)
+ fun createVideo(context: Context): Uri {
+ return getPermissionAndStageMedia(
+ context,
+ R.raw.test_video,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+ "video/mp4"
+ )
+ .first
+ }
+
+ @Throws(Exception::class)
+ fun deleteMedia(context: Context, uri: Uri?) {
+ if (uri == null) {
+ return
+ }
+ try {
+ MediaProviderTestUtils.setOwner(uri, context.packageName)
+ context.contentResolver.delete(uri, Bundle.EMPTY)
+ } catch (ignored: Exception) {}
+ }
+
+ @Throws(java.lang.Exception::class)
+ private fun getPermissionAndStageMedia(
+ context: Context,
+ resId: Int,
+ collectionUri: Uri,
+ mimeType: String,
+ ): Pair<Uri, String> {
+ return callWithShellPermissionIdentity {
+ stageMedia(context, resId, collectionUri, mimeType)
+ }
+ }
+ @Throws(IOException::class)
+ private fun stageMedia(
+ context: Context,
+ resId: Int,
+ collectionUri: Uri,
+ mimeType: String,
+ ): Pair<Uri, String> {
+ val displayName = DISPLAY_NAME_PREFIX + System.nanoTime()
+ val params = MediaStoreUtils.PendingParams(collectionUri, displayName, mimeType)
+ val pendingUri = MediaStoreUtils.createPending(context, params)
+ MediaStoreUtils.openPending(context, pendingUri).use { session ->
+ context.resources.openRawResource(resId).use { source ->
+ session.openOutputStream().use { target -> FileUtils.copy(source, target) }
+ }
+ return session.publish() to displayName
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt
new file mode 100644
index 000000000..03151c9fe
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/ReviewAccessibilityServicesTest.kt
@@ -0,0 +1,241 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule
+import android.app.UiAutomation
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.platform.test.annotations.AppModeFull
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Configurator
+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
+import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull
+import java.util.regex.Pattern
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@AppModeFull(reason = "Instant apps cannot be a11y services")
+@FlakyTest
+class ReviewAccessibilityServicesTest {
+
+ private val context: Context = InstrumentationRegistry.getInstrumentation().context
+ private val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ private val testService1String = context.getString(R.string.test_accessibility_service)
+ private val testService2String = context.getString(R.string.test_accessibility_service_2)
+ private val packageName = context.packageManager.permissionControllerPackageName
+
+ companion object {
+ private const val EXPECTED_TIMEOUT_MS = 500L
+ private const val NEW_WINDOW_TIMEOUT_MILLIS: Long = 20_000
+ }
+
+ @get:Rule
+ val accessibilityServiceRule =
+ InstrumentedAccessibilityServiceTestRule(AccessibilityTestService1::class.java, false)
+
+ @get:Rule
+ val accessibilityServiceRule2 =
+ InstrumentedAccessibilityServiceTestRule(AccessibilityTestService2::class.java, false)
+
+ init {
+ Configurator.getInstance().uiAutomationFlags =
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES
+ }
+
+ @Before
+ fun assumeNotAutoTvOrWear() {
+ Assume.assumeFalse(context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ Assume.assumeFalse(
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ )
+ Assume.assumeFalse(context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH))
+ }
+
+ @After
+ fun cleanUp() {
+ uiDevice.pressHome()
+ }
+
+ @Test
+ fun testActivityShowsSingleEnabledAccessibilityService() {
+ accessibilityServiceRule.enableService()
+ startAccessibilityActivity()
+ findTestService(true)
+ findTestService2(false)
+ }
+
+ @Test
+ fun testActivityShowsMultipleEnabledAccessibilityServices() {
+ accessibilityServiceRule.enableService()
+ accessibilityServiceRule2.enableService()
+ startAccessibilityActivity()
+ findTestService(true)
+ findTestService2(true)
+ }
+
+ @Test
+ fun testClickingSettingsGoesToIndividualSettingsWhenOneServiceEnabled() {
+ accessibilityServiceRule.enableService()
+ startAccessibilityActivity()
+ clickSettings()
+ waitForSettingsButtonToDisappear()
+ findTestService(true)
+ findTestService2(false)
+ }
+
+ @Test
+ @Ignore("b/293507233")
+ fun testClickingSettingsGoesToGeneralSettingsWhenMultipleServicesEnabled() {
+ accessibilityServiceRule.enableService()
+ accessibilityServiceRule2.enableService()
+ startAccessibilityActivity()
+ clickSettings()
+ waitForSettingsButtonToDisappear()
+ findTestService(true)
+ findTestService2(true)
+ }
+
+ @Test
+ fun testClickingIndividualGoesToIndividualSettingsWhenMultipleServicesEnabled() {
+ accessibilityServiceRule.enableService()
+ accessibilityServiceRule2.enableService()
+ startAccessibilityActivity()
+ findTestService2(true)!!.click()
+ waitForSettingsButtonToDisappear()
+ findTestService2(true)
+ findTestService(false)
+ }
+
+ private fun startAccessibilityActivity() {
+ val automan =
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES)
+ doAndWaitForWindowTransition {
+ automan.adoptShellPermissionIdentity()
+ try {
+ context.startActivity(
+ Intent(Intent.ACTION_REVIEW_ACCESSIBILITY_SERVICES)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ } catch (e: Exception) {
+ throw RuntimeException("Caught exception", e)
+ } finally {
+ automan.dropShellPermissionIdentity()
+ }
+ }
+ }
+
+ private inline fun doAndWaitForWindowTransition(crossinline block: () -> Unit) {
+ val timeoutOccurred: Boolean =
+ !uiDevice.performActionAndWait(
+ { block() },
+ Until.newWindow(),
+ NEW_WINDOW_TIMEOUT_MILLIS
+ )
+
+ if (timeoutOccurred) {
+ throw RuntimeException("Timed out waiting for window transition.")
+ }
+ }
+
+ private fun findTestService(shouldBePresent: Boolean): UiObject2? {
+ return findObjectByText(shouldBePresent, testService1String)
+ }
+
+ private fun findTestService2(shouldBePresent: Boolean): UiObject2? {
+ return findObjectByText(shouldBePresent, testService2String)
+ }
+
+ private fun clickSettings() {
+ findObjectByText(true, "Settings")?.click()
+ }
+
+ private fun waitForSettingsButtonToDisappear() {
+ SystemUtil.eventually {
+ findPCObjectByClassAndText(false,
+ "android.widget.Button",
+ "Settings"
+ )
+ }
+ }
+
+ private fun findObjectByTextWithoutRetry(
+ shouldBePresent: Boolean,
+ text: String,
+ ): UiObject2? {
+ val containsWithoutCaseSelector =
+ By.text(Pattern.compile(".*$text.*", Pattern.CASE_INSENSITIVE))
+ val view =
+ if (shouldBePresent) {
+ waitFindObjectOrNull(containsWithoutCaseSelector)
+ } else {
+ waitFindObjectOrNull(containsWithoutCaseSelector, EXPECTED_TIMEOUT_MS)
+ }
+
+ assertEquals(
+ "Expected to find view with text $text: $shouldBePresent",
+ shouldBePresent,
+ view != null
+ )
+ return view
+ }
+
+ private fun findObjectByText(expected: Boolean, text: String): UiObject2? {
+ try {
+ return findObjectByTextWithoutRetry(expected, text)
+ } catch (stale: StaleObjectException) {
+ return findObjectByTextWithoutRetry(expected, text)
+ }
+ }
+
+ private fun findPCObjectByClassAndText(
+ shouldBePresent: Boolean,
+ className: String,
+ text: String
+ ): UiObject2? {
+ val selector = By.pkg(packageName)
+ .clazz(className)
+ .text(text)
+ val view = waitFindObjectOrNull(selector)
+ assertEquals(
+ "Expected to find view with packageName '$packageName' className '$className' " +
+ "text '$text' : $shouldBePresent", shouldBePresent, view != null)
+ return view
+ }
+}
+
+/** Test Accessibility Services */
+class AccessibilityTestService1 : InstrumentedAccessibilityService()
+
+class AccessibilityTestService2 : InstrumentedAccessibilityService()
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt
new file mode 100644
index 000000000..dbbdae921
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/SafetyLabelChangesJobServiceTest.kt
@@ -0,0 +1,622 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.Context
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_OTHER
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_STORE
+import android.content.pm.PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.PersistableBundle
+import android.os.Process
+import android.permission.cts.CtsNotificationListenerHelperRule
+import android.permission.cts.CtsNotificationListenerServiceUtils
+import android.permission.cts.CtsNotificationListenerServiceUtils.getNotification
+import android.permission.cts.CtsNotificationListenerServiceUtils.getNotificationForPackageAndId
+import android.permission.cts.PermissionUtils
+import android.permission.cts.TestUtils
+import android.permissionui.cts.AppMetadata.createAppMetadataWithLocationSharingNoAds
+import android.permissionui.cts.AppMetadata.createAppMetadataWithNoSharing
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.provider.DeviceConfig
+import android.safetylabel.SafetyLabelConstants
+import android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.SystemUtil.waitForBroadcasts
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+
+/** End-to-end test for SafetyLabelChangesJobService. */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+@FlakyTest
+class SafetyLabelChangesJobServiceTest : BaseUsePermissionTest() {
+
+ @get:Rule
+ val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @get:Rule
+ val safetyLabelChangeNotificationsEnabledConfig =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED,
+ true.toString()
+ )
+
+ /**
+ * This rule serves to limit the max number of safety labels that can be persisted, so that
+ * repeated tests don't overwhelm the disk storage on the device.
+ */
+ @get:Rule
+ val deviceConfigMaxSafetyLabelsPersistedPerApp =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_MAX_SAFETY_LABELS_PERSISTED_PER_APP,
+ "2"
+ )
+
+ @get:Rule
+ val deviceConfigDataSharingUpdatesPeriod =
+ DeviceConfigStateChangerRule(
+ BasePermissionTest.context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_DATA_SHARING_UPDATE_PERIOD_MILLIS,
+ "600000"
+ )
+
+ @Before
+ fun setup() {
+ val packageManager = context.packageManager
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+ Assume.assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH))
+
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP")
+ SystemUtil.runShellCommand("wm dismiss-keyguard")
+
+ CtsNotificationListenerServiceUtils.cancelNotifications(permissionControllerPackageName)
+ resetPermissionControllerAndSimulateReboot()
+ }
+
+ @After
+ fun cancelJobsAndNotifications() {
+ cancelJob(SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID)
+ cancelJob(SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID)
+ CtsNotificationListenerServiceUtils.cancelNotifications(permissionControllerPackageName)
+ }
+
+ @Test
+ fun runDetectUpdatesJob_initializesSafetyLabelsHistoryForApps() {
+ installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app install is
+ // identified and recorded.
+ runDetectUpdatesJob()
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ waitForBroadcasts()
+
+ assertNotificationNotShown()
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runNotificationJob_initializesSafetyLabelsHistoryForApps() {
+ installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app install is
+ // identified and recorded.
+ runNotificationJob()
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ waitForBroadcasts()
+
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runDetectUpdatesJob_updatesSafetyLabelHistoryForApps() {
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runDetectUpdatesJob()
+
+ assertNotificationNotShown()
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runNotificationJob_updatesSafetyLabelHistoryForApps() {
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runNotificationJob_whenLocationSharingUpdatesForLocationGrantedApps_showsNotification() {
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ waitForBroadcasts()
+ // TODO(b/279455955): Investigate why this is necessary and remove if possible.
+ Thread.sleep(500)
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ waitForBroadcasts()
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ runNotificationJob()
+
+ waitForNotificationShown()
+
+ val statusBarNotification =
+ getNotification(permissionControllerPackageName, SAFETY_LABEL_CHANGES_NOTIFICATION_ID)
+ val contentIntent = statusBarNotification!!.notification.contentIntent
+ contentIntent.send()
+
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runNotificationJob_whenNoLocationGrantedApps_doesNotShowNotification() {
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ waitForBroadcasts()
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithLocationSharingNoAds())
+ waitForBroadcasts()
+
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @Test
+ fun runNotificationJob_whenNoLocationSharingUpdates_doesNotShowNotification() {
+ installPackageViaSession(APP_APK_NAME_31, createAppMetadataWithNoSharing())
+ waitForBroadcasts()
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @Test
+ fun runNotificationJob_packageSourceUnspecified_updatesSafetyLabelHistoryForApps() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_UNSPECIFIED
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_UNSPECIFIED
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runNotificationJob_packageSourceOther_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_OTHER
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_OTHER
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @Test
+ fun runNotificationJob_packageSourceStore_updatesSafetyLabelHistoryForApps() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_STORE
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_STORE
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertDataSharingScreenHasUpdates()
+ }
+
+ @Test
+ fun runNotificationJob_packageSourceLocalFile_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_LOCAL_FILE
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_LOCAL_FILE
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @Test
+ fun runNotificationJob_packageSourceDownloadedFile_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31,
+ createAppMetadataWithLocationSharingNoAds(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun runNotificationJob_packageSourceUnspecified_aslInApk_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_UNSPECIFIED
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_UNSPECIFIED
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun runNotificationJob_packageSourceOther_aslInApk_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_OTHER
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_OTHER
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun runNotificationJob_packageSourceStore_aslInApk_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_STORE
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_STORE
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun runNotificationJob_packageSourceLocalFile_aslInApk_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_LOCAL_FILE
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_LOCAL_FILE
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @RequiresFlagsEnabled(android.content.pm.Flags.FLAG_ASL_IN_APK_APP_METADATA_SOURCE)
+ @Test
+ fun runNotificationJob_packageSourceDownloadedFile_aslInApk_doesNotShowNotification() {
+ installPackageViaSession(
+ APP_APK_NAME_31,
+ createAppMetadataWithNoSharing(),
+ PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ waitForBroadcastReceiverFinished()
+ installPackageNoBroadcast(
+ APP_APK_NAME_31_WITH_ASL,
+ packageSource = PACKAGE_SOURCE_DOWNLOADED_FILE
+ )
+ grantLocationPermission(APP_PACKAGE_NAME)
+
+ // Run the job to check whether the missing safety label for the above app update is
+ // identified and recorded.
+ runNotificationJob()
+
+ assertNotificationNotShown()
+ }
+
+ private fun grantLocationPermission(packageName: String) {
+ uiAutomation.grantRuntimePermission(
+ packageName,
+ android.Manifest.permission.ACCESS_FINE_LOCATION
+ )
+ }
+
+ private fun installPackageNoBroadcast(
+ apkName: String,
+ appMetadata: PersistableBundle? = null,
+ packageSource: Int? = null
+ ) {
+ // Disable the safety labels feature during install to simulate installing an app without
+ // receiving an update about the change to its safety label.
+ setDeviceConfigPrivacyProperty(SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false.toString())
+ installPackageViaSession(apkName, appMetadata, packageSource)
+ waitForBroadcastReceiverFinished()
+ setDeviceConfigPrivacyProperty(SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true.toString())
+ }
+
+ private fun assertDataSharingScreenHasUpdates() {
+ startAppDataSharingUpdatesActivity()
+ try {
+ findView(By.descContains(DATA_SHARING_UPDATES), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_SUBTITLE), true)
+ findView(By.textContains(UPDATES_IN_LAST_30_DAYS), true)
+ findView(By.textContains(APP_PACKAGE_NAME_SUBSTRING), true)
+ findView(By.textContains(DATA_SHARING_UPDATES_FOOTER_MESSAGE), true)
+ } finally {
+ pressBack()
+ }
+ }
+
+ companion object {
+ private const val TIMEOUT_TIME_MS = 60_000L
+ private const val SHORT_SLEEP_MS = 2000L
+
+ private const val SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID = 8
+ private const val SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID = 9
+ private const val SET_UP_SAFETY_LABEL_CHANGES_JOB =
+ "com.android.permissioncontroller.action.SET_UP_SAFETY_LABEL_CHANGES_JOB"
+ private const val SAFETY_LABEL_CHANGES_JOB_SERVICE_RECEIVER_CLASS =
+ "com.android.permissioncontroller.permission.service.v34" +
+ ".SafetyLabelChangesJobService\$Receiver"
+ private const val SAFETY_LABEL_CHANGES_NOTIFICATION_ID = 5
+ private const val JOB_STATUS_UNKNOWN = "unknown"
+ private const val JOB_STATUS_ACTIVE = "active"
+ private const val JOB_STATUS_WAITING = "waiting"
+
+ private val context: Context = InstrumentationRegistry.getTargetContext()
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private fun uiAutomation(): UiAutomation = instrumentation.uiAutomation
+ private val permissionControllerPackageName =
+ context.packageManager.permissionControllerPackageName
+ private val userId = Process.myUserHandle().identifier
+
+ @get:ClassRule
+ @JvmStatic
+ val ctsNotificationListenerHelper =
+ CtsNotificationListenerHelperRule(
+ InstrumentationRegistry.getInstrumentation().targetContext
+ )
+
+ private fun waitForNotificationShown() {
+ eventually {
+ val notification = getNotification(false)
+ assertThat(notification).isNotNull()
+ }
+ }
+
+ private fun assertNotificationNotShown() {
+ eventually {
+ val notification = getNotification(false)
+ assertThat(notification).isNull()
+ }
+ }
+
+ private fun getNotification(cancelNotification: Boolean) =
+ getNotificationForPackageAndId(
+ permissionControllerPackageName,
+ SAFETY_LABEL_CHANGES_NOTIFICATION_ID,
+ cancelNotification
+ )
+ ?.notification
+
+ private fun cancelJob(jobId: Int) {
+ SystemUtil.runShellCommandOrThrow(
+ "cmd jobscheduler cancel -u $userId $permissionControllerPackageName $jobId"
+ )
+ TestUtils.awaitJobUntilRequestedState(
+ permissionControllerPackageName,
+ jobId,
+ TIMEOUT_TIME_MS,
+ uiAutomation(),
+ JOB_STATUS_UNKNOWN
+ )
+ }
+
+ private fun runDetectUpdatesJob() {
+ startJob(SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID)
+ TestUtils.awaitJobUntilRequestedState(
+ permissionControllerPackageName,
+ SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID,
+ TIMEOUT_TIME_MS,
+ uiAutomation(),
+ JOB_STATUS_ACTIVE
+ )
+ TestUtils.awaitJobUntilRequestedState(
+ permissionControllerPackageName,
+ SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID,
+ TIMEOUT_TIME_MS,
+ uiAutomation(),
+ JOB_STATUS_UNKNOWN
+ )
+ }
+
+ private fun runNotificationJob() {
+ startJob(SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID)
+ TestUtils.awaitJobUntilRequestedState(
+ permissionControllerPackageName,
+ SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID,
+ TIMEOUT_TIME_MS,
+ uiAutomation(),
+ JOB_STATUS_ACTIVE
+ )
+ // TODO(b/266449833): In theory we should only have to wait for "waiting" here, but
+ // sometimes jobscheduler returns "unknown".
+ TestUtils.awaitJobUntilRequestedState(
+ permissionControllerPackageName,
+ SAFETY_LABEL_CHANGES_PERIODIC_NOTIFICATION_JOB_ID,
+ TIMEOUT_TIME_MS,
+ uiAutomation(),
+ JOB_STATUS_WAITING,
+ JOB_STATUS_UNKNOWN
+ )
+ }
+
+ private fun startJob(jobId: Int) {
+ val runJobCmd =
+ "cmd jobscheduler run -u $userId -f " + "$permissionControllerPackageName $jobId"
+ try {
+ SystemUtil.runShellCommandOrThrow(runJobCmd)
+ } catch (e: Throwable) {
+ throw RuntimeException(e)
+ }
+ }
+
+ private fun resetPermissionControllerAndSimulateReboot() {
+ PermissionUtils.resetPermissionControllerJob(
+ uiAutomation(),
+ permissionControllerPackageName,
+ SAFETY_LABEL_CHANGES_DETECT_UPDATES_JOB_ID,
+ TIMEOUT_TIME_MS,
+ SET_UP_SAFETY_LABEL_CHANGES_JOB,
+ SAFETY_LABEL_CHANGES_JOB_SERVICE_RECEIVER_CLASS
+ )
+ }
+
+ private fun waitForBroadcastReceiverFinished() {
+ waitForBroadcasts()
+ // Add a short sleep to ensure that the SafetyLabelChangedBroadcastReceiver finishes its
+ // work based according to the current feature flag value before changing the flag
+ // value.
+ // While `waitForBroadcasts()` waits for broadcasts to be dispatched, it will not wait
+ // for
+ // the receivers' `onReceive` to finish.
+ Thread.sleep(SHORT_SLEEP_MS)
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/SafetyProtectionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/SafetyProtectionTest.kt
new file mode 100644
index 000000000..541ea9d16
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/SafetyProtectionTest.kt
@@ -0,0 +1,120 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.content.res.Resources
+import android.provider.DeviceConfig
+import androidx.test.filters.FlakyTest
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule
+import com.android.modules.utils.build.SdkLevel
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/** Tests for Safety Protection related features. This feature should only be enabled on T+. */
+@FlakyTest
+class SafetyProtectionTest : BaseUsePermissionTest() {
+ @get:Rule
+ val safetyProtectionEnabled =
+ DeviceConfigStateChangerRule(
+ context,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ SAFETY_PROTECTION_ENABLED_FLAG,
+ true.toString()
+ )
+
+ @Before
+ fun setup() {
+ assumeFalse(isAutomotive)
+ assumeFalse(isTv)
+ assumeFalse(isWatch)
+ }
+
+ @Ignore("b/276944839")
+ @Test
+ fun testSafetyProtectionSectionView_safetyProtection_belowT() {
+ assumeFalse("Safety Protection should only be enabled on T+", SdkLevel.isAtLeastT())
+ installPackageViaSession(APP_APK_NAME_31)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ findView(By.res(SAFETY_PROTECTION_DISPLAY_TEXT), false)
+ }
+ }
+
+ @Test
+ fun testSafetyProtectionSectionView_safetyProtectionDisabled_aboveT() {
+ assumeTrue("Safety Protection should only be enabled on T+", SdkLevel.isAtLeastT())
+ setDeviceConfigPrivacyProperty(SAFETY_PROTECTION_ENABLED_FLAG, false.toString())
+ installPackageViaSession(APP_APK_NAME_31)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ findView(By.res(SAFETY_PROTECTION_DISPLAY_TEXT), false)
+ }
+ }
+
+ @Test
+ fun testSafetyProtectionSectionView_safetyProtectionEnabled_aboveT() {
+ assumeTrue("Safety Protection should only be enabled on T+", SdkLevel.isAtLeastT())
+ assumeTrue(safetyProtectionResourcesExist)
+ installPackageViaSession(APP_APK_NAME_31)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ findView(By.res(SAFETY_PROTECTION_DISPLAY_TEXT), true)
+ }
+ }
+
+ @Test
+ fun testSafetyProtectionSectionView_safetyProtectionResourcesNotExist_aboveT() {
+ assumeTrue("Safety Protection should only be enabled on T+", SdkLevel.isAtLeastT())
+ assumeFalse(safetyProtectionResourcesExist)
+ installPackageViaSession(APP_APK_NAME_31)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION) {
+ findView(By.res(SAFETY_PROTECTION_DISPLAY_TEXT), false)
+ }
+ }
+
+ companion object {
+ private const val SAFETY_PROTECTION_ENABLED_FLAG = "safety_protection_enabled"
+ private const val SAFETY_PROTECTION_DISPLAY_TEXT =
+ "com.android.permissioncontroller:id/safety_protection_display_text"
+ private val safetyProtectionResourcesExist =
+ try {
+ 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) {
+ false
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt
new file mode 100644
index 000000000..614b59f3c
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/SensorBlockedBannerTest.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permissionui.cts
+
+import android.Manifest.permission_group.CAMERA as CAMERA_PERMISSION_GROUP
+import android.Manifest.permission_group.LOCATION as LOCATION_PERMISSION_GROUP
+import android.Manifest.permission_group.MICROPHONE as MICROPHONE_PERMISSION_GROUP
+import android.content.Intent
+import android.hardware.SensorPrivacyManager
+import android.hardware.SensorPrivacyManager.Sensors.CAMERA
+import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
+import android.location.LocationManager
+import android.os.Build
+import android.safetycenter.SafetyCenterManager
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.modules.utils.build.SdkLevel
+import java.util.regex.Pattern
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+
+/** Banner card display tests on sensors being blocked */
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+@FlakyTest
+class SensorBlockedBannerTest : BaseUsePermissionTest() {
+ companion object {
+ const val LOCATION = -1
+ const val DELAY_MILLIS = 3000L
+ private const val CHANGE_BUTTON = "com.android.permissioncontroller:id/button_id"
+ private const val CAMERA_TOGGLE_LABEL = "Camera access"
+ }
+
+ private val sensorPrivacyManager = context.getSystemService(SensorPrivacyManager::class.java)!!
+ private val locationManager = context.getSystemService(LocationManager::class.java)!!
+
+ private val sensorToPermissionGroup =
+ mapOf(
+ CAMERA to CAMERA_PERMISSION_GROUP,
+ MICROPHONE to MICROPHONE_PERMISSION_GROUP,
+ LOCATION to LOCATION_PERMISSION_GROUP
+ )
+
+ private val permToTitle =
+ mapOf(
+ CAMERA to "blocked_camera_title",
+ MICROPHONE to "blocked_microphone_title",
+ LOCATION to "blocked_location_title"
+ )
+
+ @Before
+ fun setup() {
+ Assume.assumeFalse(isTv)
+ Assume.assumeFalse(isWatch)
+ // TODO(b/203784852) Auto will eventually support the blocked sensor banner, but there won't
+ // be support in T or below
+ Assume.assumeFalse(isAutomotive)
+ installPackage(APP_APK_PATH_31)
+ }
+
+ private fun navigateAndTest(sensor: Int) {
+ val permissionGroup = sensorToPermissionGroup.getOrDefault(sensor, "Break")
+ val intent =
+ Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
+ .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permissionGroup)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ runWithShellPermissionIdentity { context.startActivity(intent) }
+ val bannerTitle = permToTitle.getOrDefault(sensor, "Break")
+ waitFindObject(By.text(getPermissionControllerString(bannerTitle)))
+ }
+
+ private fun runSensorTest(sensor: Int) {
+ var blocked = false
+ try {
+ blocked = isSensorPrivacyEnabled(sensor)
+ if (!blocked) {
+ setSensor(sensor, true)
+ }
+ navigateAndTest(sensor)
+ } finally {
+ if (!blocked) {
+ setSensor(sensor, false)
+ }
+ }
+ }
+
+ @Test
+ fun testCameraCardDisplayed() {
+ Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(CAMERA))
+ runSensorTest(CAMERA)
+ }
+
+ @Test
+ fun testMicCardDisplayed() {
+ Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(MICROPHONE))
+ runSensorTest(MICROPHONE)
+ }
+
+ @Test
+ fun testLocationCardDisplayed() {
+ runSensorTest(LOCATION)
+ }
+
+ @Test
+ fun testCardClickOpenPrivacyControls() {
+ Assume.assumeTrue(SdkLevel.isAtLeastT())
+ Assume.assumeTrue(sensorPrivacyManager.supportsSensorToggle(CAMERA))
+ val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)
+ Assume.assumeNotNull(safetyCenterManager)
+
+ var isSafetyCenterEnabled = false
+ runWithShellPermissionIdentity {
+ isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled
+ }
+ Assume.assumeTrue(isSafetyCenterEnabled)
+ // Disable global camera toggle
+ val blocked = isSensorPrivacyEnabled(CAMERA)
+ if (!blocked) {
+ setSensor(CAMERA, true)
+ }
+ // verify sensor card is shown for blocked camera
+ navigateAndTest(CAMERA)
+ click(By.res(CHANGE_BUTTON))
+ // Enable global camera toggle and verify
+ waitFindObject(By.text(CAMERA_TOGGLE_LABEL)).click()
+ assertTrue(!isSensorPrivacyEnabled(CAMERA))
+ }
+
+ private fun setSensor(sensor: Int, enable: Boolean) {
+ if (sensor == LOCATION) {
+ runWithShellPermissionIdentity {
+ locationManager.setLocationEnabledForUser(
+ !enable,
+ android.os.Process.myUserHandle()
+ )
+ if (enable) {
+ try {
+ val closePattern = Pattern.compile("close", Pattern.CASE_INSENSITIVE)
+ waitFindObjectOrNull(By.text(closePattern), DELAY_MILLIS)?.click()
+ } catch (e: Exception) {
+ // Do nothing, warning didn't show up so test can proceed
+ }
+ }
+ }
+ } else {
+ runWithShellPermissionIdentity {
+ sensorPrivacyManager.setSensorPrivacy(
+ SensorPrivacyManager.Sources.OTHER,
+ sensor,
+ enable
+ )
+ }
+ }
+ }
+
+ private fun isSensorPrivacyEnabled(sensor: Int): Boolean {
+ return if (sensor == LOCATION) {
+ callWithShellPermissionIdentity { !locationManager.isLocationEnabled() }
+ } else {
+ callWithShellPermissionIdentity { sensorPrivacyManager.isSensorPrivacyEnabled(sensor) }
+ }
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt b/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt
new file mode 100644
index 000000000..f9f9e8cd2
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/StartForFutureActivity.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package android.permissionui.cts
+
+import android.app.Activity
+import android.app.Instrumentation
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import android.util.LruCache
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.atomic.AtomicInteger
+
+class StartForFutureActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ if (savedInstanceState != null) {
+ Log.w(TAG, "Activity was recreated. (Perhaps due to a configuration change?)")
+ }
+ }
+
+ fun startActivityForFuture(
+ intent: Intent,
+ future: CompletableFuture<Instrumentation.ActivityResult>
+ ) {
+ val requestCode = nextRequestCode.getAndIncrement()
+ futures.put(requestCode, future)
+ startActivityForResult(intent, requestCode)
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ val future =
+ futures.remove(requestCode)
+ ?: throw IllegalStateException(
+ "StartForFutureActivity received an activity result with an unknown requestCode"
+ )
+ future.complete(Instrumentation.ActivityResult(resultCode, data))
+ finish()
+ }
+
+ companion object {
+ private val TAG = StartForFutureActivity::class.simpleName
+ private var nextRequestCode = AtomicInteger(1)
+ private val futures = LruCache<Int, CompletableFuture<Instrumentation.ActivityResult>>(10)
+ }
+}
diff --git a/tests/cts/permissionui/src/android/permissionui/cts/TestInstallerActivity.kt b/tests/cts/permissionui/src/android/permissionui/cts/TestInstallerActivity.kt
new file mode 100644
index 000000000..bae332a3c
--- /dev/null
+++ b/tests/cts/permissionui/src/android/permissionui/cts/TestInstallerActivity.kt
@@ -0,0 +1,21 @@
+/*
+ * 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 android.permissionui.cts
+
+import android.app.Activity
+
+class TestInstallerActivity : Activity()
diff --git a/tests/cts/role/Android.bp b/tests/cts/role/Android.bp
new file mode 100644
index 000000000..e392109db
--- /dev/null
+++ b/tests/cts/role/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2018 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: "CtsRoleTestCases",
+ defaults: ["mts-target-sdk-version-current"],
+ sdk_version: "test_current",
+ min_sdk_version: "30",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "android.permission.flags-aconfig-java-export",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "Harrier",
+ "platform-test-annotations",
+ "truth",
+ ],
+
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts-permission",
+ "mcts-permission",
+ ],
+
+ data: [
+ ":CtsRoleTestApp",
+ ":CtsRoleTestApp28",
+ ":CtsRoleTestApp33WithoutInCallService",
+ ":CtsRoleTestAppForProfile",
+ ],
+}
diff --git a/tests/cts/role/AndroidManifest.xml b/tests/cts/role/AndroidManifest.xml
new file mode 100644
index 000000000..a8c8c8e3d
--- /dev/null
+++ b/tests/cts/role/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+ <application>
+
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name=".WaitForResultActivity"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.app.role.cts"
+ android:label="CTS tests of android.app.role">
+ </instrumentation>
+</manifest>
diff --git a/tests/cts/role/AndroidTest.xml b/tests/cts/role/AndroidTest.xml
new file mode 100644
index 000000000..a53fa62d0
--- /dev/null
+++ b/tests/cts/role/AndroidTest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Config for CTS role test cases">
+
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="permissions" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="run_on_sdk_sandbox" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.permission.apex" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsRoleTestCases.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts-role" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts-role"/>
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsRoleTestApp.apk->/data/local/tmp/cts-role/CtsRoleTestApp.apk" />
+ <option name="push" value="CtsRoleTestApp28.apk->/data/local/tmp/cts-role/CtsRoleTestApp28.apk" />
+ <option name="push" value="CtsRoleTestApp33WithoutInCallService.apk->/data/local/tmp/cts-role/CtsRoleTestApp33WithoutInCallService.apk" />
+ <option name="push" value="CtsRoleTestAppForProfile.apk->/data/local/tmp/cts-role/CtsRoleTestAppForProfile.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.app.role.cts" />
+ <option name="runtime-hint" value="5m" />
+ </test>
+</configuration>
diff --git a/tests/cts/role/CtsRoleTestApp/Android.bp b/tests/cts/role/CtsRoleTestApp/Android.bp
new file mode 100644
index 000000000..1270e490b
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2018 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_helper_app {
+ name: "CtsRoleTestApp",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "30",
+ srcs: [
+ "src/**/*.java"
+ ],
+}
diff --git a/tests/cts/role/CtsRoleTestApp/AndroidManifest.xml b/tests/cts/role/CtsRoleTestApp/AndroidManifest.xml
new file mode 100644
index 000000000..b2dfca961
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp/AndroidManifest.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts.app">
+
+ <uses-permission android:name="android.permission.SEND_SMS" />
+
+ <application android:label="CtsRoleTestApp">
+
+ <activity
+ android:name=".RequestRoleActivity"
+ android:exported="true" />
+
+ <activity
+ android:name=".IsRoleHeldActivity"
+ android:exported="true" />
+
+ <activity
+ android:name=".ChangeDefaultDialerActivity"
+ android:exported="true" />
+
+ <activity
+ android:name=".ChangeDefaultSmsActivity"
+ android:exported="true" />
+
+ <!-- Dialer -->
+ <activity
+ android:name=".DialerDialActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="tel" />
+ </intent-filter>
+ </activity>
+ <service
+ android:name=".DialerInCallService"
+ android:permission="android.permission.BIND_INCALL_SERVICE"
+ android:exported="true">
+ <meta-data
+ android:name="android.telecom.IN_CALL_SERVICE_UI"
+ android:value="true"/>
+ <meta-data
+ android:name="android.telecom.IN_CALL_SERVICE_CAR_MODE_UI"
+ android:value="false"/>
+ <intent-filter>
+ <action android:name="android.telecom.InCallService" />
+ </intent-filter>
+ </service>
+ <!-- Sms -->
+ <activity
+ android:name=".SmsSendToActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.SENDTO" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="smsto" />
+ </intent-filter>
+ </activity>
+ <service
+ android:name=".SmsRespondViaMessageService"
+ android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="smsto" />
+ </intent-filter>
+ </service>
+ <receiver
+ android:name=".SmsDelieverReceiver"
+ android:permission="android.permission.BROADCAST_SMS"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.SMS_DELIVER" />
+ </intent-filter>
+ </receiver>
+ <receiver
+ android:name=".SmsWapPushDelieverReceiver"
+ android:permission="android.permission.BROADCAST_WAP_PUSH"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
+ <data android:mimeType="application/vnd.wap.mms-message" />
+ </intent-filter>
+ </receiver>
+
+ <!-- Browser -->
+ <activity
+ android:name=".BrowserActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ </intent-filter>
+ </activity>
+
+ <!-- Assistant -->
+ <activity
+ android:name=".AssistantActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.ASSIST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java
new file mode 100644
index 000000000..89cafa001
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultDialerActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telecom.TelecomManager;
+
+/**
+ * An activity that tries to change the default dialer app.
+ */
+public class ChangeDefaultDialerActivity extends Activity {
+
+ private static final int REQUEST_CODE_CHANGE_DEFAULT_DIALER = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
+ .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
+ startActivityForResult(intent, REQUEST_CODE_CHANGE_DEFAULT_DIALER);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_CHANGE_DEFAULT_DIALER) {
+ setResult(resultCode, data);
+ finish();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java
new file mode 100644
index 000000000..00559bf44
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/ChangeDefaultSmsActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Telephony;
+
+/**
+ * An activity that tries to change the default SMS app.
+ */
+public class ChangeDefaultSmsActivity extends Activity {
+
+ private static final int REQUEST_CODE_CHANGE_DEFAULT_SMS = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
+ .putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName);
+ startActivityForResult(intent, REQUEST_CODE_CHANGE_DEFAULT_SMS);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_CHANGE_DEFAULT_SMS) {
+ setResult(resultCode, data);
+ finish();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/IsRoleHeldActivity.java b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/IsRoleHeldActivity.java
new file mode 100644
index 000000000..8e97f9f24
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/IsRoleHeldActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app;
+
+import android.app.Activity;
+import android.app.role.RoleManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+/**
+ * An activity that checks whether a role is held.
+ */
+public class IsRoleHeldActivity extends Activity {
+
+ private static final String EXTRA_IS_ROLE_HELD = "android.app.role.cts.app.extra.IS_ROLE_HELD";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String roleName = getIntent().getStringExtra(Intent.EXTRA_ROLE_NAME);
+ if (TextUtils.isEmpty(roleName)) {
+ throw new IllegalArgumentException("Role name in extras cannot be null or empty");
+ }
+
+ RoleManager roleManager = getSystemService(RoleManager.class);
+ setResult(RESULT_OK, new Intent()
+ .putExtra(EXTRA_IS_ROLE_HELD, roleManager.isRoleHeld(roleName)));
+ finish();
+ }
+}
diff --git a/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java
new file mode 100644
index 000000000..b2d69e044
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp/src/android/app/role/cts/app/RequestRoleActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app;
+
+import android.app.Activity;
+import android.app.role.RoleManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+/**
+ * An activity that requests a role.
+ */
+public class RequestRoleActivity extends Activity {
+
+ private static final int REQUEST_CODE_REQUEST_ROLE = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ String roleName = getIntent().getStringExtra(Intent.EXTRA_ROLE_NAME);
+ RoleManager roleManager = getSystemService(RoleManager.class);
+ Intent intent = roleManager.createRequestRoleIntent(roleName);
+ startActivityForResult(intent, REQUEST_CODE_REQUEST_ROLE);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_REQUEST_ROLE) {
+ setResult(resultCode, data);
+ finish();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/tests/cts/role/CtsRoleTestApp28/Android.bp b/tests/cts/role/CtsRoleTestApp28/Android.bp
new file mode 100644
index 000000000..dc8239edb
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp28/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2019 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_helper_app {
+ name: "CtsRoleTestApp28",
+ min_sdk_version: "28",
+ target_sdk_version: "28",
+
+ srcs: [
+ "src/**/*.java"
+ ],
+}
diff --git a/tests/cts/role/CtsRoleTestApp28/AndroidManifest.xml b/tests/cts/role/CtsRoleTestApp28/AndroidManifest.xml
new file mode 100644
index 000000000..8113b2676
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp28/AndroidManifest.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts.app28">
+
+ <uses-permission android:name="android.permission.SEND_SMS"/>
+
+ <application android:label="CtsRoleTestApp28">
+
+ <activity android:name=".ChangeDefaultDialerActivity"
+ android:exported="true"/>
+
+ <activity android:name=".ChangeDefaultSmsActivity"
+ android:exported="true"/>
+
+ <!-- Dialer -->
+ <activity android:name=".DialerDialActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="tel"/>
+ </intent-filter>
+ </activity>
+
+ <!-- Sms -->
+ <activity android:name=".SmsSendToActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.SENDTO"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="smsto"/>
+ </intent-filter>
+ </activity>
+ <service android:name=".SmsRespondViaMessageService"
+ android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.RESPOND_VIA_MESSAGE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:scheme="smsto"/>
+ </intent-filter>
+ </service>
+ <receiver android:name=".SmsDelieverReceiver"
+ android:permission="android.permission.BROADCAST_SMS"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.SMS_DELIVER"/>
+ </intent-filter>
+ </receiver>
+ <receiver android:name=".SmsWapPushDelieverReceiver"
+ android:permission="android.permission.BROADCAST_WAP_PUSH"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER"/>
+ <data android:mimeType="application/vnd.wap.mms-message"/>
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java b/tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java
new file mode 100644
index 000000000..5d1c47cfc
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultDialerActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app28;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telecom.TelecomManager;
+
+/**
+ * An activity that tries to change the default dialer app.
+ */
+public class ChangeDefaultDialerActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
+ .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
+ startActivity(intent);
+ }
+ }
+}
diff --git a/tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java b/tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java
new file mode 100644
index 000000000..37819bbec
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp28/src/android/app/role/cts/app28/ChangeDefaultSmsActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts.app28;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.telecom.TelecomManager;
+
+/**
+ * An activity that tries to change the default SMS app.
+ */
+public class ChangeDefaultSmsActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
+ .putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName);
+ startActivity(intent);
+ }
+ }
+}
diff --git a/tests/cts/role/CtsRoleTestApp33WithoutInCallService/Android.bp b/tests/cts/role/CtsRoleTestApp33WithoutInCallService/Android.bp
new file mode 100644
index 000000000..7cce565af
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp33WithoutInCallService/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2021 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_helper_app {
+ name: "CtsRoleTestApp33WithoutInCallService",
+ min_sdk_version: "30",
+ target_sdk_version: "33",
+}
diff --git a/tests/cts/role/CtsRoleTestApp33WithoutInCallService/AndroidManifest.xml b/tests/cts/role/CtsRoleTestApp33WithoutInCallService/AndroidManifest.xml
new file mode 100644
index 000000000..a6504adae
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestApp33WithoutInCallService/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts.app33WithoutInCallService">
+ <application android:label="CtsRoleTestApp33WithoutInCallService">
+ <!-- Dialer -->
+ <activity
+ android:name=".DialerDialActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="tel" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/role/CtsRoleTestAppForProfile/Android.bp b/tests/cts/role/CtsRoleTestAppForProfile/Android.bp
new file mode 100644
index 000000000..1d9a1e6e8
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestAppForProfile/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsRoleTestAppForProfile",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "30",
+}
diff --git a/tests/cts/role/CtsRoleTestAppForProfile/AndroidManifest.xml b/tests/cts/role/CtsRoleTestAppForProfile/AndroidManifest.xml
new file mode 100644
index 000000000..05413e55f
--- /dev/null
+++ b/tests/cts/role/CtsRoleTestAppForProfile/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.role.cts.appForProfile">
+
+ <application android:label="CtsRoleTestAppForProfile">
+ <!-- Browser -->
+ <activity
+ android:name=".BrowserActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/cts/role/OWNERS b/tests/cts/role/OWNERS
new file mode 100644
index 000000000..fb6099cf7
--- /dev/null
+++ b/tests/cts/role/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 137825
+
+include platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/cts/role/TEST_MAPPING b/tests/cts/role/TEST_MAPPING
new file mode 100644
index 000000000..46b148e68
--- /dev/null
+++ b/tests/cts/role/TEST_MAPPING
@@ -0,0 +1,65 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "mainline-presubmit": [
+ {
+ "name": "CtsRoleTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsRoleTestCases",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsRoleTestCases"
+ }
+ ],
+ "mainline-postsubmit": [
+ {
+ "name": "CtsRoleTestCases[com.google.android.permission.apex]",
+ "options": [
+ // TODO(b/238677748): These two tests currently fails on R base image
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#openDefaultAppListThenIsNotDefaultAppInList"
+ },
+ {
+ "exclude-filter": "android.app.role.cts.RoleManagerTest#removeSmsRoleHolderThenPermissionIsRevoked"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/cts/role/src/android/app/role/cts/RoleControllerManagerTest.kt b/tests/cts/role/src/android/app/role/cts/RoleControllerManagerTest.kt
new file mode 100644
index 000000000..0fd7c0152
--- /dev/null
+++ b/tests/cts/role/src/android/app/role/cts/RoleControllerManagerTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+package android.app.role.cts
+
+import android.app.Instrumentation
+import android.app.role.RoleManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Process
+import android.provider.Settings
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SdkSuppress
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.compatibility.common.util.ThrowingSupplier
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests RoleControllerManager APIs exposed on [RoleManager]. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+class RoleControllerManagerTest {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context: Context = instrumentation.context
+ private val packageManager: PackageManager = context.packageManager
+ private val roleManager: RoleManager = context.getSystemService(RoleManager::class.java)!!
+
+ @Before
+ fun installApp() {
+ installPackage(APP_APK_PATH)
+ }
+
+ @After
+ fun uninstallApp() {
+ uninstallPackage(APP_PACKAGE_NAME)
+ }
+
+ @Test
+ fun appIsVisibleForRole() {
+ assumeRoleIsVisible()
+ assertAppIsVisibleForRole(APP_PACKAGE_NAME, ROLE_NAME, true)
+ }
+
+ @Test
+ fun settingsIsNotVisibleForHomeRole() {
+ // Settings should never show as a possible home app even if qualified.
+ val settingsPackageName =
+ packageManager
+ .resolveActivity(
+ Intent(Settings.ACTION_SETTINGS),
+ PackageManager.MATCH_DEFAULT_ONLY or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ )!!
+ .activityInfo
+ .packageName
+ assertAppIsVisibleForRole(settingsPackageName, RoleManager.ROLE_HOME, false)
+ }
+
+ @Test
+ fun appIsNotVisibleForInvalidRole() {
+ assertAppIsVisibleForRole(APP_PACKAGE_NAME, "invalid", false)
+ }
+
+ @Test
+ fun invalidAppIsNotVisibleForRole() {
+ assertAppIsVisibleForRole("invalid", ROLE_NAME, false)
+ }
+
+ private fun assertAppIsVisibleForRole(
+ packageName: String,
+ roleName: String,
+ expectedIsVisible: Boolean
+ ) {
+ runWithShellPermissionIdentity {
+ val future = CompletableFuture<Boolean>()
+ roleManager.isApplicationVisibleForRole(
+ roleName,
+ packageName,
+ context.mainExecutor,
+ Consumer { future.complete(it) }
+ )
+ val isVisible = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ assertThat(isVisible).isEqualTo(expectedIsVisible)
+ }
+ }
+
+ private fun assumeRoleIsVisible() {
+ assumeTrue(isRoleVisible(ROLE_NAME))
+ }
+
+ @Test
+ fun systemGalleryRoleIsNotVisible() {
+ // The system gallery role should always be hidden.
+ assertThat(isRoleVisible(SYSTEM_GALLERY_ROLE_NAME)).isEqualTo(false)
+ }
+
+ @Test
+ fun invalidRoleIsNotVisible() {
+ assertThat(isRoleVisible("invalid")).isEqualTo(false)
+ }
+
+ private fun isRoleVisible(roleName: String): Boolean =
+ runWithShellPermissionIdentity(
+ ThrowingSupplier {
+ val future = CompletableFuture<Boolean>()
+ roleManager.isRoleVisible(
+ roleName,
+ context.mainExecutor,
+ Consumer { future.complete(it) }
+ )
+ future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ }
+ )
+
+ private fun installPackage(apkPath: String) {
+ assertEquals(
+ "Success",
+ runShellCommandOrThrow(
+ "pm install -r --user ${Process.myUserHandle().identifier} $apkPath"
+ )
+ .trim()
+ )
+ }
+
+ private fun uninstallPackage(packageName: String) {
+ assertEquals(
+ "Success",
+ runShellCommand("pm uninstall --user ${Process.myUserHandle().identifier} $packageName")
+ .trim()
+ )
+ }
+
+ companion object {
+ private const val ROLE_NAME = RoleManager.ROLE_BROWSER
+ private const val APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk"
+ private const val APP_PACKAGE_NAME = "android.app.role.cts.app"
+ private const val SYSTEM_GALLERY_ROLE_NAME = "android.app.role.SYSTEM_GALLERY"
+ private const val TIMEOUT_MILLIS = 15 * 1000L
+ }
+}
diff --git a/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
new file mode 100644
index 000000000..b64de5665
--- /dev/null
+++ b/tests/cts/role/src/android/app/role/cts/RoleManagerTest.java
@@ -0,0 +1,1437 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject;
+import static com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObjectOrNull;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.Instrumentation;
+import android.app.role.OnRoleHoldersChangedListener;
+import android.app.role.RoleManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.permission.flags.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.telephony.TelephonyManager;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.Until;
+
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile;
+import com.android.bedstead.nene.types.OptionalBoolean;
+import com.android.compatibility.common.util.DisableAnimationRule;
+import com.android.compatibility.common.util.FreezeRotationRule;
+import com.android.compatibility.common.util.TestUtils;
+import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.compatibility.common.util.UiAutomatorUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+
+/**
+ * Tests {@link RoleManager}.
+ */
+@RunWith(BedsteadJUnit4.class)
+public class RoleManagerTest {
+
+ private static final long TIMEOUT_MILLIS = 15 * 1000;
+
+ private static final long UNEXPECTED_TIMEOUT_MILLIS = 1000;
+
+ private static final String ROLE_NAME = RoleManager.ROLE_BROWSER;
+ private static final String ROLE_PHONE_NAME = RoleManager.ROLE_DIALER;
+ private static final String ROLE_SMS_NAME = RoleManager.ROLE_SMS;
+ private static final String ROLE_SHORT_LABEL = "Browser app";
+
+ private static final String APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk";
+ private static final String APP_PACKAGE_NAME = "android.app.role.cts.app";
+ private static final String APP_LABEL = "CtsRoleTestApp";
+ private static final String APP_FOR_PROFILE_APK_PATH =
+ "/data/local/tmp/cts-role/CtsRoleTestAppForProfile.apk";
+ private static final String APP_FOR_PROFILE_PACKAGE_NAME = "android.app.role.cts.appForProfile";
+ private static final String APP_FOR_PROFILE = "CtsRoleTestAppForProfile";
+ private static final String APP_IS_ROLE_HELD_ACTIVITY_NAME = APP_PACKAGE_NAME
+ + ".IsRoleHeldActivity";
+ private static final String APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD = APP_PACKAGE_NAME
+ + ".extra.IS_ROLE_HELD";
+ private static final String APP_REQUEST_ROLE_ACTIVITY_NAME = APP_PACKAGE_NAME
+ + ".RequestRoleActivity";
+ private static final String APP_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME = APP_PACKAGE_NAME
+ + ".ChangeDefaultDialerActivity";
+ private static final String APP_CHANGE_DEFAULT_SMS_ACTIVITY_NAME = APP_PACKAGE_NAME
+ + ".ChangeDefaultSmsActivity";
+
+ private static final String APP_28_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp28.apk";
+ private static final String APP_28_PACKAGE_NAME = "android.app.role.cts.app28";
+ private static final String APP_28_LABEL = "CtsRoleTestApp28";
+ private static final String APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME = APP_28_PACKAGE_NAME
+ + ".ChangeDefaultDialerActivity";
+ private static final String APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME = APP_28_PACKAGE_NAME
+ + ".ChangeDefaultSmsActivity";
+
+ private static final String APP_33_WITHOUT_INCALLSERVICE_APK_PATH =
+ "/data/local/tmp/cts-role/CtsRoleTestApp33WithoutInCallService.apk";
+ private static final String APP_33_WITHOUT_INCALLSERVICE_PACKAGE_NAME =
+ "android.app.role.cts.app33WithoutInCallService";
+
+ private static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
+ "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
+
+ private static final Instrumentation sInstrumentation =
+ InstrumentationRegistry.getInstrumentation();
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private static final PackageManager sPackageManager = sContext.getPackageManager();
+ private static final RoleManager sRoleManager = sContext.getSystemService(RoleManager.class);
+ private static final boolean sIsAutomotive = sPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ private static final boolean sIsTelevision = sPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEVISION);
+ private static final boolean sIsWatch = sPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_WATCH);
+
+ private static final BySelector ENHANCED_CONFIRMATION_DIALOG_SELECTOR =
+ By.res("com.android.permissioncontroller:id/enhanced_confirmation_dialog_title");
+ // TODO(b/327528959): consider using resource selectors for Wear too, once the underlying
+ // issue is handled.
+ private static final BySelector NEGATIVE_BUTTON_SELECTOR =
+ sIsWatch ? By.text("Cancel") : By.res("android:id/button2");
+ private static final BySelector POSITIVE_BUTTON_SELECTOR =
+ sIsWatch ? By.text("Set as default") : By.res("android:id/button1");
+ private static final BySelector DONT_ASK_AGAIN_TOGGLE_SELECTOR =
+ sIsWatch
+ ? By.text("Don\u2019t ask again")
+ : By.res("com.android.permissioncontroller:id/dont_ask_again");
+
+ @Rule
+ public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public DisableAnimationRule mDisableAnimationRule = new DisableAnimationRule();
+
+ @Rule
+ public FreezeRotationRule mFreezeRotationRule = new FreezeRotationRule();
+
+ @Rule
+ public ActivityTestRule<WaitForResultActivity> mActivityRule =
+ new ActivityTestRule<>(WaitForResultActivity.class);
+
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private String mRoleHolder;
+
+ @Before
+ public void saveRoleHolder() throws Exception {
+ List<String> roleHolders = getRoleHolders(ROLE_NAME);
+ mRoleHolder = !roleHolders.isEmpty() ? roleHolders.get(0) : null;
+
+ if (Objects.equals(mRoleHolder, APP_PACKAGE_NAME)) {
+ removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ mRoleHolder = null;
+ }
+ }
+
+ @After
+ public void restoreRoleHolder() throws Exception {
+ removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ if (mRoleHolder != null) {
+ addRoleHolder(ROLE_NAME, mRoleHolder);
+ }
+
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Before
+ public void installApp() throws Exception {
+ installPackage(APP_APK_PATH);
+ installPackage(APP_28_APK_PATH);
+ installPackage(APP_33_WITHOUT_INCALLSERVICE_APK_PATH);
+ }
+
+ @After
+ public void uninstallApp() throws Exception {
+ uninstallPackage(APP_PACKAGE_NAME);
+ uninstallPackage(APP_28_PACKAGE_NAME);
+ uninstallPackage(APP_33_WITHOUT_INCALLSERVICE_PACKAGE_NAME);
+ }
+
+ @Before
+ public void wakeUpScreen() throws IOException {
+ runShellCommand(sInstrumentation, "input keyevent KEYCODE_WAKEUP");
+ }
+
+ @Before
+ public void closeNotificationShade() {
+ sContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ }
+
+ @Test
+ public void requestRoleIntentHasPermissionControllerPackage() throws Exception {
+ Intent intent = sRoleManager.createRequestRoleIntent(ROLE_NAME);
+
+ assertThat(intent.getPackage()).isEqualTo(
+ sPackageManager.getPermissionControllerPackageName());
+ }
+
+ @Test
+ public void requestRoleIntentHasExtraRoleName() throws Exception {
+ Intent intent = sRoleManager.createRequestRoleIntent(ROLE_NAME);
+
+ assertThat(intent.getStringExtra(Intent.EXTRA_ROLE_NAME)).isEqualTo(ROLE_NAME);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void requestRoleAndDenyThenIsNotRoleHolder() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void requestRoleAndAllowThenIsRoleHolder() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(true);
+
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true);
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void requestRoleThenBlockRequestRoleDialogByRestrictedSettingDialog() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+ assumeFalse(sIsWatch || sIsAutomotive || sIsTelevision);
+ runWithShellPermissionIdentity(
+ () -> setEnhancedConfirmationRestrictedAppOpMode(sContext, APP_PACKAGE_NAME,
+ AppOpsManager.MODE_ERRORED));
+
+ requestRole(ROLE_SMS_NAME);
+ waitFindObject(ENHANCED_CONFIRMATION_DIALOG_SELECTOR, TIMEOUT_MILLIS);
+
+ pressBack();
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void requestRoleFirstTimeNoDontAskAgain() throws Exception {
+ requestRole(ROLE_NAME);
+ UiObject2 dontAskAgainCheck = findDontAskAgainCheck(false);
+
+ assertThat(dontAskAgainCheck).isNull();
+
+ respondToRoleRequest(false);
+ }
+
+ @Test
+ @FlakyTest
+ public void requestRoleAndDenyThenHasDontAskAgain() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+
+ requestRole(ROLE_NAME);
+ UiObject2 dontAskAgainCheck = findDontAskAgainCheck();
+
+ assertThat(dontAskAgainCheck).isNotNull();
+
+ respondToRoleRequest(false);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void requestRoleAndDenyWithDontAskAgainReturnsCanceled() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+
+ requestRole(ROLE_NAME);
+ findDontAskAgainCheck().click();
+ Pair<Integer, Intent> result = clickButtonAndWaitForResult(true);
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @FlakyTest
+ public void requestRoleAndDenyWithDontAskAgainThenDeniedAutomatically() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+
+ requestRole(ROLE_NAME);
+ findDontAskAgainCheck().click();
+ clickButtonAndWaitForResult(true);
+
+ requestRole(ROLE_NAME);
+ Pair<Integer, Intent> result = waitForResult();
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @FlakyTest
+ public void requestRoleAndDenyWithDontAskAgainAndClearDataThenShowsUiWithoutDontAskAgain()
+ throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+
+ requestRole(ROLE_NAME);
+ findDontAskAgainCheck().click();
+ clickButtonAndWaitForResult(true);
+ // Wait for the RequestRoleActivity inside the test app to be removed from our task so that
+ // when the test app is force stopped, our task isn't force finished and our
+ // WaitForResultActivity can survive.
+ Thread.sleep(5000);
+
+ clearPackageData(APP_PACKAGE_NAME);
+ // Wait for the don't ask again to be forgotten.
+ Thread.sleep(10000);
+
+ TestUtils.waitUntil("Find and respond to request role UI", () -> {
+ requestRole(ROLE_NAME);
+ UiObject2 cancelButton = waitFindObjectOrNull(NEGATIVE_BUTTON_SELECTOR);
+ if (cancelButton == null) {
+ // Dialog not found, try again later.
+ return false;
+ }
+ UiObject2 dontAskAgainCheck = findDontAskAgainCheck(false);
+
+ assertThat(dontAskAgainCheck).isNull();
+
+ respondToRoleRequest(false);
+ return true;
+ });
+ }
+
+ @Test
+ @FlakyTest
+ public void requestRoleAndDenyWithDontAskAgainAndReinstallThenShowsUiWithoutDontAskAgain()
+ throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(false);
+
+ requestRole(ROLE_NAME);
+ findDontAskAgainCheck().click();
+ clickButtonAndWaitForResult(true);
+ // Wait for the RequestRoleActivity inside the test app to be removed from our task so that
+ // when the test app is uninstalled, our task isn't force finished and our
+ // WaitForResultActivity can survive.
+ Thread.sleep(5000);
+
+ uninstallPackage(APP_PACKAGE_NAME);
+ // Wait for the don't ask again to be forgotten.
+ Thread.sleep(10000);
+ installPackage(APP_APK_PATH);
+
+ TestUtils.waitUntil("Find and respond to request role UI", () -> {
+ requestRole(ROLE_NAME);
+ UiObject2 cancelButton = waitFindObjectOrNull(NEGATIVE_BUTTON_SELECTOR);
+ if (cancelButton == null) {
+ // Dialog not found, try again later.
+ return false;
+ }
+ UiObject2 dontAskAgainCheck = findDontAskAgainCheck(false);
+
+ assertThat(dontAskAgainCheck).isNull();
+
+ respondToRoleRequest(false);
+ return true;
+ });
+ }
+
+ @Test
+ public void requestInvalidRoleThenDeniedAutomatically() throws Exception {
+ requestRole("invalid");
+ Pair<Integer, Intent> result = waitForResult();
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void requestUnqualifiedRoleThenDeniedAutomatically() throws Exception {
+ requestRole(RoleManager.ROLE_HOME);
+ Pair<Integer, Intent> result = waitForResult();
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void requestAssistantRoleThenDeniedAutomatically() throws Exception {
+ requestRole(RoleManager.ROLE_ASSISTANT);
+ Pair<Integer, Intent> result = waitForResult();
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void requestHoldingRoleThenAllowedAutomatically() throws Exception {
+ requestRole(ROLE_NAME);
+ respondToRoleRequest(true);
+
+ requestRole(ROLE_NAME);
+ Pair<Integer, Intent> result = waitForResult();
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_OK);
+ }
+
+ private void requestRole(@NonNull String roleName) {
+ Intent intent = new Intent()
+ .setComponent(new ComponentName(APP_PACKAGE_NAME, APP_REQUEST_ROLE_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_ROLE_NAME, roleName);
+ mActivityRule.getActivity().startActivityToWaitForResult(intent);
+ }
+
+ private void respondToRoleRequest(boolean allow)
+ throws InterruptedException, UiObjectNotFoundException {
+ if (allow) {
+ waitFindObject(By.text(APP_LABEL)).click();
+ }
+ Pair<Integer, Intent> result = clickButtonAndWaitForResult(allow);
+ int expectedResult = allow ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
+
+ assertThat(result.first).isEqualTo(expectedResult);
+ }
+
+ @Nullable
+ private UiObject2 findDontAskAgainCheck(boolean expected) throws UiObjectNotFoundException {
+ return expected
+ ? waitFindObject(DONT_ASK_AGAIN_TOGGLE_SELECTOR)
+ : waitFindObjectOrNull(DONT_ASK_AGAIN_TOGGLE_SELECTOR, UNEXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Nullable
+ private UiObject2 findDontAskAgainCheck() throws UiObjectNotFoundException {
+ return findDontAskAgainCheck(true);
+ }
+
+ @NonNull
+ private Pair<Integer, Intent> clickButtonAndWaitForResult(boolean positive)
+ throws InterruptedException, UiObjectNotFoundException {
+ waitFindObject(positive ? POSITIVE_BUTTON_SELECTOR : NEGATIVE_BUTTON_SELECTOR).click();
+ return waitForResult();
+ }
+
+ @NonNull
+ private Pair<Integer, Intent> waitForResult() throws InterruptedException {
+ return mActivityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS);
+ }
+
+ private void clearPackageData(@NonNull String packageName) {
+ runShellCommand("pm clear --user " + Process.myUserHandle().getIdentifier() + " "
+ + packageName);
+ }
+
+ private void installPackage(@NonNull String apkPath) {
+ installPackage(apkPath, Process.myUserHandle());
+ }
+
+ private void installPackage(@NonNull String apkPath, UserHandle user) {
+ runShellCommandOrThrow("pm install -r --user " + user.getIdentifier() + " " + apkPath);
+ }
+
+ private void uninstallPackage(@NonNull String packageName) {
+ uninstallPackage(packageName, Process.myUserHandle());
+ }
+
+ private void uninstallPackage(@NonNull String packageName, UserHandle user) {
+ runShellCommand("pm uninstall --user " + user.getIdentifier() + " " + packageName);
+ }
+
+ @Test
+ public void targetCurrentSdkAndChangeDefaultDialerThenDeniedAutomatically() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER));
+
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startActivityToWaitForResult(new Intent()
+ .setComponent(new ComponentName(APP_PACKAGE_NAME,
+ APP_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME));
+ Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ public void targetCurrentSdkAndChangeDefaultSmsThenDeniedAutomatically() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startActivityToWaitForResult(new Intent()
+ .setComponent(new ComponentName(APP_PACKAGE_NAME,
+ APP_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME));
+ Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void targetSdk28AndChangeDefaultDialerAndAllowThenIsDefaultDialer() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER));
+
+ sContext.startActivity(new Intent()
+ .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+ APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ waitFindObject(By.text(APP_28_LABEL)).click();
+ waitFindObject(POSITIVE_BUTTON_SELECTOR).click();
+
+ // TODO(b/149037075): Use TelecomManager.getDefaultDialerPackage() once the bug is fixed.
+ //TelecomManager telecomManager = sContext.getSystemService(TelecomManager.class);
+ //TestUtils.waitUntil("App is not set as default dialer app", () -> Objects.equals(
+ // telecomManager.getDefaultDialerPackage(), APP_28_PACKAGE_NAME));
+ TestUtils.waitUntil("App is not set as default dialer app", () ->
+ getRoleHolders(RoleManager.ROLE_DIALER).contains(APP_28_PACKAGE_NAME));
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void targetSdk28AndChangeDefaultSmsAndAllowThenIsDefaultSms() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ sContext.startActivity(new Intent()
+ .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+ APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ waitFindObject(By.text(APP_28_LABEL)).click();
+ waitFindObject(POSITIVE_BUTTON_SELECTOR).click();
+
+ TestUtils.waitUntil("App is not set as default sms app", () -> Objects.equals(
+ Telephony.Sms.getDefaultSmsPackage(sContext), APP_28_PACKAGE_NAME));
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void targetSdk28AndChangeDefaultDialerForAnotherAppThenDeniedAutomatically()
+ throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER));
+
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startActivityToWaitForResult(new Intent()
+ .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+ APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME));
+ Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void targetSdk28AndChangeDefaultSmsForAnotherAppThenDeniedAutomatically()
+ throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startActivityToWaitForResult(new Intent()
+ .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+ APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME));
+ Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void
+ targetSdk28AndChangeDefaultDialerForAnotherAppAsHolderAndAllowThenTheOtherAppIsDefaultDialer()
+ throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER));
+
+ addRoleHolder(RoleManager.ROLE_DIALER, APP_28_PACKAGE_NAME);
+ sContext.startActivity(new Intent()
+ .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+ APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ waitFindObject(By.text(APP_LABEL)).click();
+ waitFindObject(POSITIVE_BUTTON_SELECTOR).click();
+
+ // TODO(b/149037075): Use TelecomManager.getDefaultDialerPackage() once the bug is fixed.
+ //TelecomManager telecomManager = sContext.getSystemService(TelecomManager.class);
+ //TestUtils.waitUntil("App is not set as default dialer app", () -> Objects.equals(
+ // telecomManager.getDefaultDialerPackage(), APP_PACKAGE_NAME));
+ TestUtils.waitUntil("App is not set as default dialer app", () ->
+ getRoleHolders(RoleManager.ROLE_DIALER).contains(APP_PACKAGE_NAME));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ public void testHoldDialerRoleRequirementWithInCallServiceAndSdk()
+ throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER));
+ // target below sdk 33 without InCallService component can hold dialer role
+ addRoleHolder(
+ RoleManager.ROLE_DIALER, APP_28_PACKAGE_NAME, true);
+ assertIsRoleHolder(
+ RoleManager.ROLE_DIALER, APP_28_PACKAGE_NAME, true);
+ // target sdk 33 without InCallService component cannot hold dialer role
+ addRoleHolder(
+ RoleManager.ROLE_DIALER, APP_33_WITHOUT_INCALLSERVICE_PACKAGE_NAME, false);
+ assertIsRoleHolder(
+ RoleManager.ROLE_DIALER, APP_33_WITHOUT_INCALLSERVICE_PACKAGE_NAME, false);
+ // target sdk 33 with InCallService component can hold dialer role
+ addRoleHolder(
+ RoleManager.ROLE_DIALER, APP_PACKAGE_NAME, true);
+ assertIsRoleHolder(
+ RoleManager.ROLE_DIALER, APP_PACKAGE_NAME, true);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void
+ targetSdk28AndChangeDefaultSmsForAnotherAppAsHolderAndAllowThenTheOtherAppIsDefaultSms()
+ throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ addRoleHolder(RoleManager.ROLE_SMS, APP_28_PACKAGE_NAME);
+ sContext.startActivity(new Intent()
+ .setComponent(new ComponentName(APP_28_PACKAGE_NAME,
+ APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, APP_PACKAGE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ waitFindObject(By.text(APP_LABEL)).click();
+ waitFindObject(POSITIVE_BUTTON_SELECTOR).click();
+
+ TestUtils.waitUntil("App is not set as default sms app", () -> Objects.equals(
+ Telephony.Sms.getDefaultSmsPackage(sContext), APP_PACKAGE_NAME));
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppDetailsThenIsNotDefaultApp() throws Exception {
+ runWithShellPermissionIdentity(() -> sContext.startActivity(new Intent(
+ Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)));
+
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false).hasDescendant(By.text(APP_LABEL)));
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL)));
+ }
+
+ pressBack();
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName =
+ "VanillaIceCream")
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppDetailsOnHandHeldThenRestrictedAppIsNotSelectableAsDefaultApp()
+ throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER));
+ assumeFalse(sIsWatch || sIsAutomotive || sIsTelevision);
+ runWithShellPermissionIdentity(
+ () -> setEnhancedConfirmationRestrictedAppOpMode(sContext, APP_PACKAGE_NAME,
+ AppOpsManager.MODE_ERRORED));
+
+ runWithShellPermissionIdentity(() -> sContext.startActivity(new Intent(
+ Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, ROLE_PHONE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)));
+
+ waitFindObject(By.text(APP_LABEL).enabled(false)).clickAndWait(Until.newWindow(),
+ TIMEOUT_MILLIS);
+
+ waitFindObject(ENHANCED_CONFIRMATION_DIALOG_SELECTOR, TIMEOUT_MILLIS);
+ pressBack();
+
+ pressBack();
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppDetailsAndSetDefaultAppThenIsDefaultApp() throws Exception {
+ runWithShellPermissionIdentity(() -> sContext.startActivity(new Intent(
+ Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)));
+ waitForIdle();
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false).hasDescendant(
+ By.text(APP_LABEL))).click();
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL))).click();
+ }
+
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(APP_LABEL)));
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(APP_LABEL)));
+ }
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true);
+
+ pressBack();
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppDetailsAndSetDefaultAppAndSetAnotherThenIsNotDefaultApp()
+ throws Exception {
+ runWithShellPermissionIdentity(() -> sContext.startActivity(new Intent(
+ Intent.ACTION_MANAGE_DEFAULT_APP)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .putExtra(Intent.EXTRA_ROLE_NAME, ROLE_NAME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK)));
+ waitForIdle();
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false).hasDescendant(
+ By.text(APP_LABEL))).click();
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(APP_LABEL)));
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL))).click();
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(APP_LABEL)));
+ }
+ waitForIdle();
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false)).click();
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).enabled(true)
+ .checked(false))).click();
+ }
+
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false).hasDescendant(By.text(APP_LABEL)));
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL)));
+ }
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+
+ pressBack();
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppListThenHasDefaultApp() throws Exception {
+ sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
+
+ waitFindObject(By.text(ROLE_SHORT_LABEL));
+
+ pressBack();
+ }
+
+ @FlakyTest
+ @Test
+ public void openDefaultAppListThenIsNotDefaultAppInList() throws Exception {
+ sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
+
+ assertThat(waitFindObjectOrNull(By.text(APP_LABEL), UNEXPECTED_TIMEOUT_MILLIS))
+ .isNull();
+
+ pressBack();
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppListAndSetDefaultAppThenIsDefaultApp() throws Exception {
+ sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
+ waitForIdle();
+ waitFindObject(By.text(ROLE_SHORT_LABEL)).click();
+ waitForIdle();
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false).hasDescendant(
+ By.text(APP_LABEL))).click();
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL))).click();
+ }
+
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(APP_LABEL)));
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(APP_LABEL)));
+ }
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true);
+
+ pressBack();
+ pressBack();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @EnsureHasPrivateProfile(installInstrumentedApp = OptionalBoolean.TRUE)
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ codeName = "VanillaIceCream")
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppListAndSetDefaultAppThenIsDefaultAppForPrivateSpace()
+ throws Exception {
+ // Private space is not supported on Watch right now and there are no existing plans yet.
+ if (sIsWatch) {
+ return;
+ }
+
+ UserHandle privateProfile = sDeviceState.privateProfile().userHandle();
+ assertThat(privateProfile).isNotNull();
+ installPackage(APP_APK_PATH, privateProfile);
+ installPackage(APP_FOR_PROFILE_APK_PATH, privateProfile);
+ addRoleHolderAsUser(ROLE_NAME, APP_FOR_PROFILE_PACKAGE_NAME, privateProfile);
+
+ sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
+ waitForIdle();
+
+ waitFindObject(By.hasDescendant(By.text(APP_FOR_PROFILE))).click();
+
+ waitForIdle();
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL))).click();
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(APP_LABEL)));
+
+ assertIsRoleHolderAsUser(ROLE_NAME, APP_PACKAGE_NAME, true, privateProfile);
+
+ pressBack();
+ pressBack();
+
+ uninstallPackage(APP_PACKAGE_NAME, privateProfile);
+ uninstallPackage(APP_FOR_PROFILE_APK_PATH, privateProfile);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void openDefaultAppListAndSetDefaultAppThenIsDefaultAppInList() throws Exception {
+ sContext.startActivity(new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
+ waitForIdle();
+ waitFindObject(By.text(ROLE_SHORT_LABEL)).click();
+ waitForIdle();
+ if (sIsWatch) {
+ waitFindObject(By.clickable(true).checked(false).hasDescendant(
+ By.text(APP_LABEL))).click();
+ waitFindObject(By.clickable(true).checked(true).hasDescendant(By.text(APP_LABEL)));
+ } else {
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(false))
+ .hasDescendant(By.text(APP_LABEL))).click();
+ waitFindObject(By.clickable(true).hasDescendant(By.checkable(true).checked(true))
+ .hasDescendant(By.text(APP_LABEL)));
+ }
+ pressBack();
+
+ waitFindObject(By.text(APP_LABEL));
+
+ pressBack();
+ }
+
+ private void setEnhancedConfirmationRestrictedAppOpMode(@NonNull Context context,
+ @NonNull String packageName, int mode)
+ throws PackageManager.NameNotFoundException {
+ AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
+ appOpsManager.setMode(AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS,
+ context.getPackageManager().getApplicationInfo(packageName, 0).uid,
+ packageName, mode);
+ }
+
+ private static void waitForIdle() {
+ UiAutomatorUtils.getUiDevice().waitForIdle();
+ }
+
+ private static void pressBack() {
+ UiAutomatorUtils.getUiDevice().pressBack();
+ waitForIdle();
+ }
+
+ @Test
+ public void roleIsAvailable() {
+ assertThat(sRoleManager.isRoleAvailable(ROLE_NAME)).isTrue();
+ }
+
+ @Test
+ public void dontAddRoleHolderThenRoleIsNotHeld() throws Exception {
+ assertRoleIsHeld(ROLE_NAME, false);
+ }
+
+ @Test
+ public void addRoleHolderThenRoleIsHeld() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ assertRoleIsHeld(ROLE_NAME, true);
+ }
+
+ @Test
+ public void addAndRemoveRoleHolderThenRoleIsNotHeld() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ assertRoleIsHeld(ROLE_NAME, false);
+ }
+
+ private void assertRoleIsHeld(@NonNull String roleName, boolean isHeld)
+ throws InterruptedException {
+ Intent intent = new Intent()
+ .setComponent(new ComponentName(APP_PACKAGE_NAME, APP_IS_ROLE_HELD_ACTIVITY_NAME))
+ .putExtra(Intent.EXTRA_ROLE_NAME, roleName);
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startActivityToWaitForResult(intent);
+ Pair<Integer, Intent> result = activity.waitForActivityResult(TIMEOUT_MILLIS);
+
+ assertThat(result.first).isEqualTo(Activity.RESULT_OK);
+ assertThat(result.second).isNotNull();
+ assertThat(result.second.hasExtra(APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD)).isTrue();
+ assertThat(result.second.getBooleanExtra(APP_IS_ROLE_HELD_EXTRA_IS_ROLE_HELD, false))
+ .isEqualTo(isHeld);
+ }
+
+ @Test
+ public void dontAddRoleHolderThenIsNotRoleHolder() throws Exception {
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void addRoleHolderThenIsRoleHolder() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, true);
+ }
+
+ @Test
+ public void addAndRemoveRoleHolderThenIsNotRoleHolder() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ removeRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void addAndClearRoleHoldersThenIsNotRoleHolder() throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+ clearRoleHolders(ROLE_NAME);
+
+ assertIsRoleHolder(ROLE_NAME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void addInvalidRoleHolderThenFails() throws Exception {
+ addRoleHolder("invalid", APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void addUnqualifiedRoleHolderThenFails() throws Exception {
+ addRoleHolder(RoleManager.ROLE_HOME, APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void removeInvalidRoleHolderThenFails() throws Exception {
+ removeRoleHolder("invalid", APP_PACKAGE_NAME, false);
+ }
+
+ @Test
+ public void clearInvalidRoleHoldersThenFails() throws Exception {
+ clearRoleHolders("invalid", false);
+ }
+
+ @Test
+ public void addOnRoleHoldersChangedListenerAndAddRoleHolderThenIsNotified() throws Exception {
+ assertOnRoleHoldersChangedListenerIsNotified(() -> addRoleHolder(ROLE_NAME,
+ APP_PACKAGE_NAME));
+ }
+
+ @Test
+ public void addOnRoleHoldersChangedListenerAndRemoveRoleHolderThenIsNotified()
+ throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ assertOnRoleHoldersChangedListenerIsNotified(() -> removeRoleHolder(ROLE_NAME,
+ APP_PACKAGE_NAME));
+ }
+
+ @Test
+ public void addOnRoleHoldersChangedListenerAndClearRoleHoldersThenIsNotified()
+ throws Exception {
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ assertOnRoleHoldersChangedListenerIsNotified(() -> clearRoleHolders(ROLE_NAME));
+ }
+
+ private void assertOnRoleHoldersChangedListenerIsNotified(@NonNull ThrowingRunnable runnable)
+ throws Exception {
+ ListenerFuture future = new ListenerFuture();
+ UserHandle user = Process.myUserHandle();
+ runWithShellPermissionIdentity(() -> sRoleManager.addOnRoleHoldersChangedListenerAsUser(
+ sContext.getMainExecutor(), future, user));
+ Pair<String, UserHandle> result;
+ try {
+ runnable.run();
+ result = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } finally {
+ runWithShellPermissionIdentity(() ->
+ sRoleManager.removeOnRoleHoldersChangedListenerAsUser(future, user));
+ }
+
+ assertThat(result.first).isEqualTo(ROLE_NAME);
+ assertThat(result.second).isEqualTo(user);
+ }
+
+ @Test
+ public void addAndRemoveOnRoleHoldersChangedListenerAndAddRoleHolderThenIsNotNotified()
+ throws Exception {
+ ListenerFuture future = new ListenerFuture();
+ UserHandle user = Process.myUserHandle();
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.addOnRoleHoldersChangedListenerAsUser(sContext.getMainExecutor(), future,
+ user);
+ sRoleManager.removeOnRoleHoldersChangedListenerAsUser(future, user);
+ });
+ addRoleHolder(ROLE_NAME, APP_PACKAGE_NAME);
+
+ try {
+ future.get(UNEXPECTED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ // Expected
+ return;
+ }
+ throw new AssertionError("OnRoleHoldersChangedListener was notified after removal");
+ }
+
+ @Test
+ public void setRoleNamesFromControllerShouldRequireManageRolesFromControllerPermission() {
+ assertRequiresManageRolesFromControllerPermission(
+ () -> sRoleManager.setRoleNamesFromController(Collections.emptyList()),
+ "setRoleNamesFromController");
+ }
+
+ @Test
+ public void addRoleHolderFromControllerShouldRequireManageRolesFromControllerPermission() {
+ assertRequiresManageRolesFromControllerPermission(
+ () -> sRoleManager.addRoleHolderFromController(ROLE_NAME, APP_PACKAGE_NAME),
+ "addRoleHolderFromController");
+ }
+
+ @Test
+ public void removeRoleHolderFromControllerShouldRequireManageRolesFromControllerPermission() {
+ assertRequiresManageRolesFromControllerPermission(
+ () -> sRoleManager.removeRoleHolderFromController(ROLE_NAME, APP_PACKAGE_NAME),
+ "removeRoleHolderFromController");
+ }
+
+ @Test
+ public void getHeldRolesFromControllerShouldRequireManageRolesFromControllerPermission() {
+ assertRequiresManageRolesFromControllerPermission(
+ () -> sRoleManager.getHeldRolesFromController(APP_PACKAGE_NAME),
+ "getHeldRolesFromController");
+ }
+
+ private void assertRequiresManageRolesFromControllerPermission(@NonNull Runnable runnable,
+ @NonNull String methodName) {
+ try {
+ runnable.run();
+ } catch (SecurityException e) {
+ if (e.getMessage().contains(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)) {
+ // Expected
+ return;
+ }
+ throw e;
+ }
+ fail("RoleManager." + methodName + "() should require "
+ + PERMISSION_MANAGE_ROLES_FROM_CONTROLLER);
+ }
+
+ @Test
+ public void manageRolesFromControllerPermissionShouldBeDeclaredByPermissionController()
+ throws PackageManager.NameNotFoundException {
+ PermissionInfo permissionInfo = sPackageManager.getPermissionInfo(
+ PERMISSION_MANAGE_ROLES_FROM_CONTROLLER, 0);
+
+ assertThat(permissionInfo.packageName).isEqualTo(
+ sPackageManager.getPermissionControllerPackageName());
+ assertThat(permissionInfo.getProtection()).isEqualTo(PermissionInfo.PROTECTION_SIGNATURE);
+ assertThat(permissionInfo.getProtectionFlags()).isEqualTo(0);
+ }
+
+ @Test
+ public void smsRoleHasHolder() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ assertThat(getRoleHolders(RoleManager.ROLE_SMS)).isNotEmpty();
+ }
+
+ @Test
+ public void addSmsRoleHolderThenPermissionIsGranted() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
+
+ assertThat(sPackageManager.checkPermission(android.Manifest.permission.SEND_SMS,
+ APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_GRANTED);
+ }
+
+ @Test
+ public void removeSmsRoleHolderThenPermissionIsRevoked() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ String smsRoleHolder = getRoleHolders(RoleManager.ROLE_SMS).get(0);
+ addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
+ addRoleHolder(RoleManager.ROLE_SMS, smsRoleHolder);
+
+ assertThat(sPackageManager.checkPermission(android.Manifest.permission.SEND_SMS,
+ APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_DENIED);
+ }
+
+ @Test
+ public void removeSmsRoleHolderThenDialerRolePermissionIsRetained() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER)
+ && sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ addRoleHolder(RoleManager.ROLE_DIALER, APP_PACKAGE_NAME);
+ String smsRoleHolder = getRoleHolders(RoleManager.ROLE_SMS).get(0);
+ addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
+ addRoleHolder(RoleManager.ROLE_SMS, smsRoleHolder);
+
+ assertThat(sPackageManager.checkPermission(android.Manifest.permission.SEND_SMS,
+ APP_PACKAGE_NAME)).isEqualTo(PackageManager.PERMISSION_GRANTED);
+ }
+
+ @Test
+ public void packageManagerGetDefaultBrowserBackedByRole() throws Exception {
+ addRoleHolder(RoleManager.ROLE_BROWSER, APP_PACKAGE_NAME);
+
+ assertThat(sPackageManager.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId()))
+ .isEqualTo(APP_PACKAGE_NAME);
+ }
+
+ @Test
+ @FlakyTest(bugId = 288468003, detail = "CtsRoleTestCases is breaching 20min SLO")
+ public void packageManagerSetDefaultBrowserBackedByRole() throws Exception {
+ callWithShellPermissionIdentity(() -> sPackageManager.setDefaultBrowserPackageNameAsUser(
+ APP_PACKAGE_NAME, UserHandle.myUserId()));
+
+ assertIsRoleHolder(RoleManager.ROLE_BROWSER, APP_PACKAGE_NAME, true);
+ }
+
+ @Test
+ public void telephonySmsGetDefaultSmsPackageBackedByRole() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
+
+ assertThat(Telephony.Sms.getDefaultSmsPackage(sContext)).isEqualTo(APP_PACKAGE_NAME);
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ codeName = "VanillaIceCream")
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED)
+ public void telephonyManagerGetEmergencyAssistancePackageNameBackedByRole() throws Exception {
+ TelephonyManager telephonyManager = sContext.getSystemService(TelephonyManager.class);
+ List<String> emergencyRoleHolders = getRoleHolders(RoleManager.ROLE_EMERGENCY);
+
+ if (telephonyManager.isVoiceCapable()
+ && callWithShellPermissionIdentity(() ->
+ telephonyManager.isEmergencyAssistanceEnabled())) {
+ String emergencyAssistancePackageName = callWithShellPermissionIdentity(() ->
+ telephonyManager.getEmergencyAssistancePackageName());
+ if (emergencyRoleHolders.isEmpty()) {
+ assertThat(emergencyAssistancePackageName).isNull();
+ } else {
+ assertThat(emergencyRoleHolders).hasSize(1);
+ assertThat(emergencyAssistancePackageName).isEqualTo(emergencyRoleHolders.get(0));
+ }
+ } else {
+ assertThrows(IllegalStateException.class, () ->
+ callWithShellPermissionIdentity(() ->
+ telephonyManager.getEmergencyAssistancePackageName()));
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+ @Test
+ public void cannotBypassRoleQualificationWithoutPermission() throws Exception {
+ assertThrows(SecurityException.class, () ->
+ sRoleManager.setBypassingRoleQualification(true));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+ @Test
+ public void bypassRoleQualificationThenCanAddUnqualifiedRoleHolder() throws Exception {
+ assertThat(sRoleManager.isRoleAvailable(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER))
+ .isTrue();
+
+ runWithShellPermissionIdentity(() -> sRoleManager.setBypassingRoleQualification(true));
+ try {
+ assertThat(callWithShellPermissionIdentity(() ->
+ sRoleManager.isBypassingRoleQualification())).isTrue();
+
+ // The System Activity Recognizer role requires a system app, so this won't succeed
+ // without bypassing role qualification.
+ addRoleHolder(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, APP_PACKAGE_NAME);
+
+ assertThat(getRoleHolders(RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER))
+ .contains(APP_PACKAGE_NAME);
+ } finally {
+ runWithShellPermissionIdentity(() -> sRoleManager.setBypassingRoleQualification(false));
+ }
+ assertThat(callWithShellPermissionIdentity(() ->
+ sRoleManager.isBypassingRoleQualification())).isFalse();
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void cannotGetDefaultApplicationWithoutPermission() throws Exception {
+ assertThrows(SecurityException.class, ()->
+ sRoleManager.getDefaultApplication(
+ RoleManager.ROLE_SMS));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void getDefaultApplicationChecksRoles() throws Exception {
+ runWithShellPermissionIdentity(() ->
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.getDefaultApplication(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER)));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void getDefaultApplicationReadsRole() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ addRoleHolder(RoleManager.ROLE_SMS, APP_PACKAGE_NAME);
+ runWithShellPermissionIdentity(() -> {
+ assertThat(sRoleManager.getDefaultApplication(RoleManager.ROLE_SMS))
+ .isEqualTo(APP_PACKAGE_NAME);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void cannotSetDefaultApplicationWithoutPermission() throws Exception {
+ CallbackFuture future = new CallbackFuture();
+ assertThrows(SecurityException.class, ()->
+ sRoleManager.setDefaultApplication(
+ RoleManager.ROLE_SMS, APP_PACKAGE_NAME, 0,
+ sContext.getMainExecutor(), future));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void setDefaultApplicationChecksRoles() throws Exception {
+ CallbackFuture future = new CallbackFuture();
+ runWithShellPermissionIdentity(() ->
+ assertThrows(IllegalArgumentException.class, () ->
+ sRoleManager.setDefaultApplication(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER, APP_PACKAGE_NAME, 0,
+ sContext.getMainExecutor(), future)));
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake")
+ @Test
+ public void setDefaultApplicationSetsRole() throws Exception {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ CallbackFuture future = new CallbackFuture();
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setDefaultApplication(
+ RoleManager.ROLE_SMS, APP_PACKAGE_NAME, 0,
+ sContext.getMainExecutor(), future);
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ assertThat(sRoleManager.getRoleHolders(RoleManager.ROLE_SMS))
+ .containsExactly(APP_PACKAGE_NAME);
+ });
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM,
+ codeName = "VanillaIceCream")
+ @Test
+ public void testSetAndGetRoleFallbackEnabled() {
+ assumeTrue(sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS));
+
+ runWithShellPermissionIdentity(() -> {
+ sRoleManager.setRoleFallbackEnabled(RoleManager.ROLE_SMS, true);
+ assertThat(sRoleManager.isRoleFallbackEnabled(RoleManager.ROLE_SMS)).isTrue();
+ });
+ }
+
+ @NonNull
+ private List<String> getRoleHolders(@NonNull String roleName) throws Exception {
+ return callWithShellPermissionIdentity(() -> sRoleManager.getRoleHolders(roleName));
+ }
+
+ @NonNull
+ private List<String> getRoleHoldersAsUser(@NonNull String roleName, UserHandle userHandle)
+ throws Exception {
+ return callWithShellPermissionIdentity(
+ () -> sRoleManager.getRoleHoldersAsUser(roleName, userHandle));
+ }
+
+ private void assertIsRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ boolean shouldBeRoleHolder) throws Exception {
+ List<String> packageNames = getRoleHolders(roleName);
+
+ if (shouldBeRoleHolder) {
+ assertThat(packageNames).contains(packageName);
+ } else {
+ assertThat(packageNames).doesNotContain(packageName);
+ }
+ }
+
+ private void assertIsRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+ boolean shouldBeRoleHolder, UserHandle userHandle) throws Exception {
+ List<String> packageNames = getRoleHoldersAsUser(roleName, userHandle);
+
+ if (shouldBeRoleHolder) {
+ assertThat(packageNames).contains(packageName);
+ } else {
+ assertThat(packageNames).doesNotContain(packageName);
+ }
+ }
+
+ private void addRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ boolean expectSuccess) throws Exception {
+ addRoleHolderAsUser(roleName, packageName, Process.myUserHandle(), expectSuccess);
+ }
+
+ private void addRoleHolder(@NonNull String roleName, @NonNull String packageName)
+ throws Exception {
+ addRoleHolder(roleName, packageName, true);
+ }
+
+ private void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+ UserHandle userHandle, boolean expectSuccess) throws Exception {
+ CallbackFuture future = new CallbackFuture();
+ runWithShellPermissionIdentity(() -> sRoleManager.addRoleHolderAsUser(roleName,
+ packageName, 0, userHandle, sContext.getMainExecutor(), future));
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isEqualTo(expectSuccess);
+ }
+
+ private void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
+ UserHandle userHandle) throws Exception {
+ addRoleHolderAsUser(roleName, packageName, userHandle, true);
+ }
+
+ private void removeRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ boolean expectSuccess) throws Exception {
+ CallbackFuture future = new CallbackFuture();
+ runWithShellPermissionIdentity(() -> sRoleManager.removeRoleHolderAsUser(roleName,
+ packageName, 0, Process.myUserHandle(), sContext.getMainExecutor(), future));
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isEqualTo(expectSuccess);
+ }
+
+ private void removeRoleHolder(@NonNull String roleName, @NonNull String packageName)
+ throws Exception {
+ removeRoleHolder(roleName, packageName, true);
+ }
+
+ private void clearRoleHolders(@NonNull String roleName, boolean expectSuccess)
+ throws Exception {
+ CallbackFuture future = new CallbackFuture();
+ runWithShellPermissionIdentity(() -> sRoleManager.clearRoleHoldersAsUser(roleName, 0,
+ Process.myUserHandle(), sContext.getMainExecutor(), future));
+ assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isEqualTo(expectSuccess);
+ }
+
+ private void clearRoleHolders(@NonNull String roleName) throws Exception {
+ clearRoleHolders(roleName, true);
+ }
+
+ private static class ListenerFuture extends CompletableFuture<Pair<String, UserHandle>>
+ implements OnRoleHoldersChangedListener {
+
+ @Override
+ public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
+ complete(new Pair<>(roleName, user));
+ }
+ }
+
+ private static class CallbackFuture extends CompletableFuture<Boolean>
+ implements Consumer<Boolean> {
+
+ @Override
+ public void accept(Boolean successful) {
+ complete(successful);
+ }
+ }
+}
diff --git a/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt
new file mode 100644
index 000000000..7e58e1848
--- /dev/null
+++ b/tests/cts/role/src/android/app/role/cts/RoleShellCommandTest.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts
+
+import android.app.role.RoleManager
+import android.os.Build
+import android.os.UserHandle
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.SdkSuppress
+import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests role shell commands. */
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S, codeName = "S")
+class RoleShellCommandTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val context = instrumentation.context
+ private val roleManager = context.getSystemService(RoleManager::class.java)!!
+ private val userId = UserHandle.myUserId()
+
+ private var roleHolder: String? = null
+ private var wasBypassingRoleQualification: Boolean = false
+
+ @Before
+ fun saveRoleHolder() {
+ roleHolder = getRoleHolders().firstOrNull()
+ if (roleHolder == APP_PACKAGE_NAME) {
+ removeRoleHolder()
+ roleHolder = null
+ }
+ }
+
+ @Before
+ fun saveBypassingRoleQualification() {
+ wasBypassingRoleQualification = isBypassingRoleQualification()
+ }
+
+ @After
+ fun restoreRoleHolder() {
+ removeRoleHolder()
+ roleHolder?.let { addRoleHolder(it) }
+ assertIsRoleHolder(false)
+ }
+
+ @After
+ fun restoreBypassingRoleQualification() {
+ setBypassingRoleQualification(wasBypassingRoleQualification)
+ }
+
+ @Before
+ fun installApp() {
+ installPackage(APP_APK_PATH)
+ }
+
+ @After
+ fun uninstallApp() {
+ uninstallPackage(APP_PACKAGE_NAME)
+ }
+
+ @Test
+ fun helpPrintsNonEmpty() {
+ assertThat(runShellCommandOrThrow("cmd role help")).isNotEmpty()
+ }
+
+ @Test
+ fun dontAddRoleHolderThenIsNotRoleHolder() {
+ assertIsRoleHolder(false)
+ }
+
+ @Test
+ fun addRoleHolderThenIsRoleHolder() {
+ addRoleHolder()
+
+ assertIsRoleHolder(true)
+ }
+
+ @Test
+ fun addAndRemoveRoleHolderThenIsNotRoleHolder() {
+ addRoleHolder()
+ removeRoleHolder()
+
+ assertIsRoleHolder(false)
+ }
+
+ @Test
+ fun addAndClearRoleHolderThenIsNotRoleHolder() {
+ addRoleHolder()
+ clearRoleHolders()
+
+ assertIsRoleHolder(false)
+ }
+
+ @Test
+ fun addInvalidRoleHolderThenFails() {
+ assertThrows(AssertionError::class.java) {
+ runShellCommandOrThrow("cmd role add-role-holder --user $userId $ROLE_NAME invalid")
+ }
+ }
+
+ @Test
+ fun addRoleHolderThenAppearsInDumpsys() {
+ addRoleHolder()
+
+ assertThat(runShellCommandOrThrow("dumpsys role")).contains(APP_PACKAGE_NAME)
+ }
+
+ @Test
+ fun setBypassingRoleQualificationToTrueThenSetsToTrue() {
+ setBypassingRoleQualification(false)
+
+ runShellCommandOrThrow("cmd role set-bypassing-role-qualification true")
+
+ assertThat(isBypassingRoleQualification()).isTrue()
+ }
+
+ @Test
+ fun setBypassingRoleQualificationToFalseThenSetsToFalse() {
+ setBypassingRoleQualification(true)
+
+ runShellCommandOrThrow("cmd role set-bypassing-role-qualification false")
+
+ assertThat(isBypassingRoleQualification()).isFalse()
+ }
+
+ private fun addRoleHolder(packageName: String = APP_PACKAGE_NAME) {
+ runShellCommandOrThrow("cmd role add-role-holder --user $userId $ROLE_NAME $packageName")
+ }
+
+ private fun removeRoleHolder(packageName: String = APP_PACKAGE_NAME) {
+ runShellCommandOrThrow("cmd role remove-role-holder --user $userId $ROLE_NAME $packageName")
+ }
+
+ private fun clearRoleHolders() {
+ runShellCommandOrThrow("cmd role clear-role-holders --user $userId $ROLE_NAME")
+ }
+
+ private fun getRoleHolders(): List<String> =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ runShellCommandOrThrow("cmd role get-role-holders --user $userId $ROLE_NAME")
+ .trim()
+ .let { if (it.isNotEmpty()) it.split(";") else emptyList() }
+ } else {
+ callWithShellPermissionIdentity { roleManager.getRoleHolders(ROLE_NAME) }
+ }
+
+ private fun assertIsRoleHolder(shouldBeRoleHolder: Boolean) {
+ val packageNames = getRoleHolders()
+ if (shouldBeRoleHolder) {
+ assertThat(packageNames).contains(APP_PACKAGE_NAME)
+ } else {
+ assertThat(packageNames).doesNotContain(APP_PACKAGE_NAME)
+ }
+ }
+
+ private fun installPackage(apkPath: String) {
+ assertThat(runShellCommandOrThrow("pm install -r --user $userId $apkPath").trim())
+ .isEqualTo("Success")
+ }
+
+ private fun uninstallPackage(packageName: String) {
+ assertThat(runShellCommandOrThrow("pm uninstall --user $userId $packageName").trim())
+ .isEqualTo("Success")
+ }
+
+ private fun isBypassingRoleQualification(): Boolean = callWithShellPermissionIdentity {
+ roleManager.isBypassingRoleQualification()
+ }
+
+ private fun setBypassingRoleQualification(value: Boolean) {
+ callWithShellPermissionIdentity { roleManager.setBypassingRoleQualification(value) }
+ }
+
+ companion object {
+ private const val ROLE_NAME = RoleManager.ROLE_BROWSER
+ private const val APP_APK_PATH = "/data/local/tmp/cts-role/CtsRoleTestApp.apk"
+ private const val APP_PACKAGE_NAME = "android.app.role.cts.app"
+ }
+}
diff --git a/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java b/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java
new file mode 100644
index 000000000..fb13423d4
--- /dev/null
+++ b/tests/cts/role/src/android/app/role/cts/WaitForResultActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.role.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An Activity that can start another Activity and wait for its result.
+ */
+public class WaitForResultActivity extends Activity {
+
+ private static final int REQUEST_CODE_WAIT_FOR_RESULT = 1;
+
+ private CountDownLatch mLatch;
+ private int mResultCode;
+ private Intent mData;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ throw new RuntimeException(
+ "Activity was recreated (perhaps due to a configuration change?) "
+ + "and this activity doesn't currently know how to gracefully handle "
+ + "configuration changes.");
+ }
+ }
+
+ public void startActivityToWaitForResult(@NonNull Intent intent) {
+ mLatch = new CountDownLatch(1);
+ startActivityForResult(intent, REQUEST_CODE_WAIT_FOR_RESULT);
+ }
+
+ @NonNull
+ public Pair<Integer, Intent> waitForActivityResult(long timeoutMillis)
+ throws InterruptedException {
+ mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+ return new Pair<>(mResultCode, mData);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ if (requestCode == REQUEST_CODE_WAIT_FOR_RESULT) {
+ mResultCode = resultCode;
+ mData = data;
+ mLatch.countDown();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+}
diff --git a/tests/cts/safetycenter/Android.bp b/tests/cts/safetycenter/Android.bp
index 78e43bedd..e49587c39 100644
--- a/tests/cts/safetycenter/Android.bp
+++ b/tests/cts/safetycenter/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -41,7 +42,7 @@ android_test {
"safety-center-pending-intents",
"safety-center-test-util-lib",
"modules-utils-build",
- "truth-prebuilt",
+ "truth",
],
test_suites: [
"cts",
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/TEST_MAPPING b/tests/cts/safetycenter/TEST_MAPPING
index e8c210a5d..3bdba6266 100644
--- a/tests/cts/safetycenter/TEST_MAPPING
+++ b/tests/cts/safetycenter/TEST_MAPPING
@@ -9,7 +9,17 @@
"name": "CtsSafetyCenterTestCases[com.google.android.permission.apex]",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "CtsSafetyCenterTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
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..4b6f0f6f9 100644
--- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
+++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourceTest.kt
@@ -17,12 +17,15 @@
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.os.Build.VERSION_CODES.VANILLA_ICE_CREAM
+import android.platform.test.annotations.RequiresFlagsEnabled
import android.safetycenter.config.SafetySource
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.truth.os.ParcelableSubject.assertThat
import androidx.test.filters.SdkSuppress
import com.android.modules.utils.build.SdkLevel
+import com.android.permission.flags.Flags
import com.android.safetycenter.testing.EqualsHashCodeToStringTester
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
@@ -75,7 +78,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)
@@ -126,6 +129,35 @@ class SafetySourceTest {
}
}
+ @RequiresFlagsEnabled(Flags.FLAG_PRIVATE_PROFILE_TITLE_API)
+ @SdkSuppress(minSdkVersion = VANILLA_ICE_CREAM, codeName = "VanillaIceCream")
+ @Test
+ fun getTitleForPrivateProfileResId_returnsTitleForPrivateProfileResIdOrThrows() {
+ if (!Flags.privateProfileTitleApi()) {
+ return
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ DYNAMIC_BAREBONE.titleForPrivateProfileResId
+ }
+ assertThat(dynamicAllOptional().titleForPrivateProfileResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ DYNAMIC_DISABLED.titleForPrivateProfileResId
+ }
+ assertThat(DYNAMIC_HIDDEN.titleForPrivateProfileResId).isEqualTo(Resources.ID_NULL)
+ assertThat(DYNAMIC_HIDDEN_WITH_SEARCH.titleForPrivateProfileResId)
+ .isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ STATIC_BAREBONE.titleForPrivateProfileResId
+ }
+ assertThat(STATIC_ALL_OPTIONAL.titleForPrivateProfileResId).isEqualTo(REFERENCE_RES_ID)
+ assertThrows(UnsupportedOperationException::class.java) {
+ ISSUE_ONLY_BAREBONE.titleForPrivateProfileResId
+ }
+ assertThrows(UnsupportedOperationException::class.java) {
+ issueOnlyAllOptional().titleForPrivateProfileResId
+ }
+ }
+
@Test
fun getSummaryResId_returnsSummaryResIdOrThrows() {
assertThat(DYNAMIC_BAREBONE.summaryResId).isEqualTo(REFERENCE_RES_ID)
@@ -259,7 +291,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 +305,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 +319,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()
@@ -360,6 +392,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -390,6 +425,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -413,6 +451,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -436,6 +477,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -459,6 +503,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -482,6 +529,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -505,6 +555,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -536,6 +589,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -559,6 +615,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -582,6 +641,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -605,6 +667,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -628,6 +693,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
)
@@ -650,6 +718,11 @@ class SafetySourceTest {
.setNotificationsAllowed(false)
.setDeduplicationGroup(DEDUPLICATION_GROUP)
.addPackageCertificateHash(HASH1)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
)
addEqualityGroup(
@@ -669,6 +742,11 @@ class SafetySourceTest {
.setNotificationsAllowed(true)
.setDeduplicationGroup("other_deduplication_group")
.addPackageCertificateHash(HASH1)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
)
// With no package cert hashes provided
@@ -688,6 +766,11 @@ class SafetySourceTest {
.setRefreshOnPageOpenAllowed(true)
.setNotificationsAllowed(true)
.setDeduplicationGroup(DEDUPLICATION_GROUP)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
)
// With longer package cert hash list
@@ -709,6 +792,11 @@ class SafetySourceTest {
.setDeduplicationGroup(DEDUPLICATION_GROUP)
.addPackageCertificateHash(HASH1)
.addPackageCertificateHash(HASH2)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
)
// With package cert hash list with different value
@@ -729,6 +817,11 @@ class SafetySourceTest {
.setNotificationsAllowed(true)
.setDeduplicationGroup(DEDUPLICATION_GROUP)
.addPackageCertificateHash(HASH2)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
)
}
@@ -785,6 +878,9 @@ class SafetySourceTest {
setDeduplicationGroup(DEDUPLICATION_GROUP)
addPackageCertificateHash(HASH1)
}
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
}
.build()
@@ -817,6 +913,11 @@ class SafetySourceTest {
.setProfile(SafetySource.PROFILE_ALL)
.setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
.setSearchTermsResId(REFERENCE_RES_ID)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
internal val STATIC_BAREBONE =
@@ -837,6 +938,11 @@ class SafetySourceTest {
.setIntentAction(INTENT_ACTION)
.setProfile(SafetySource.PROFILE_ALL)
.setSearchTermsResId(REFERENCE_RES_ID)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(REFERENCE_RES_ID)
+ }
+ }
.build()
internal val ISSUE_ONLY_BAREBONE =
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/Android.bp b/tests/functional/safetycenter/multiusers/Android.bp
index c1c7a95e7..30024221b 100644
--- a/tests/functional/safetycenter/multiusers/Android.bp
+++ b/tests/functional/safetycenter/multiusers/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -35,6 +36,7 @@ android_test {
"Harrier",
"Nene",
"TestApp",
+ "com.android.permission.flags-aconfig-java-export",
],
test_suites: [
"general-tests",
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..ff5dcc1f6 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,12 @@ 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.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
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 +33,37 @@ 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.enterprise.annotations.EnsureHasNoWorkProfile
+import com.android.bedstead.harrier.annotations.EnsureHasPrivateProfile
+import com.android.bedstead.harrier.annotations.EnsureHasNoPrivateProfile
+import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile
+import com.android.bedstead.enterprise.annotations.EnsureHasDeviceOwner
+import com.android.bedstead.enterprise.annotations.EnsureHasNoDeviceOwner
import com.android.bedstead.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 +84,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 +115,67 @@ class SafetyCenterMultiUsersTest {
@JvmField @ClassRule @Rule val deviceState: DeviceState = DeviceState()
}
- @get:Rule val disableAnimationRule = DisableAnimationRule()
-
- @get:Rule val freezeRotationRule = FreezeRotationRule()
+ @Rule @JvmField val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
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 +186,61 @@ 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 dynamicDisabledForPrivateUpdated: SafetyCenterEntry
+ get() =
+ safetyCenterEntryOkForPrivate(DYNAMIC_DISABLED_ID, deviceState.privateProfile().id())
+
+ private val dynamicHiddenForPrivateUpdated: SafetyCenterEntry
+ get() = safetyCenterEntryOkForPrivate(DYNAMIC_HIDDEN_ID, deviceState.privateProfile().id())
+
private val staticGroupBuilder =
SafetyCenterEntryGroup.Builder(STATIC_GROUP_ID, "OK")
.setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNSPECIFIED)
@@ -223,7 +266,8 @@ class SafetyCenterMultiUsersTest {
)
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
- deviceState.workProfile().userHandle()
+ deviceState.workProfile().userHandle(),
+ explicit = false
)
)
@@ -233,51 +277,97 @@ 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 =
- SafetyCenterStaticEntry.Builder("OK")
- .setSummary("OK")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
-
- private val staticEntryUpdated =
- SafetyCenterStaticEntry.Builder("Unspecified title")
- .setSummary("Unspecified summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
- .build()
-
- private val staticEntryForWorkBuilder
+ private val staticAllOptionalForPrivateBuilder
get() =
- SafetyCenterStaticEntry.Builder("Paste")
- .setSummary("OK")
+ safetyCenterTestData
+ .safetyCenterEntryDefaultStaticBuilder(
+ STATIC_ALL_OPTIONAL_ID,
+ userId = deviceState.privateProfile().id(),
+ title = "Unknown"
+ )
.setPendingIntent(
createTestActivityRedirectPendingIntentForUser(
- deviceState.workProfile().userHandle()
+ deviceState.privateProfile().userHandle(),
+ explicit = false
)
)
- private val staticEntryForWork
- get() = staticEntryForWorkBuilder.build()
+ private val staticAllOptionalForPrivate
+ get() = staticAllOptionalForPrivateBuilder.build()
- private val staticEntryForWorkPaused
+ private fun createStaticEntry(explicit: Boolean = true): SafetyCenterStaticEntry =
+ SafetyCenterStaticEntry.Builder("OK")
+ .setSummary("OK")
+ .setPendingIntent(
+ safetySourceTestData.createTestActivityRedirectPendingIntent(explicit)
+ )
+ .build()
+
+ private val staticEntryUpdated: SafetyCenterStaticEntry
get() =
- staticEntryForWorkBuilder
- // TODO(b/233188021): This needs to use the Enterprise API to override the "work"
- // keyword.
- .setSummary(safetyCenterResourcesContext.getStringByName("work_profile_paused"))
+ SafetyCenterStaticEntry.Builder("Unspecified title")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
.build()
- private val staticEntryForWorkUpdated =
- SafetyCenterStaticEntry.Builder("Unspecified title for Work")
- .setSummary("Unspecified summary")
- .setPendingIntent(safetySourceTestData.testActivityRedirectPendingIntent)
+ private fun staticEntryForWorkBuilder(title: CharSequence = "Paste", explicit: Boolean = true) =
+ SafetyCenterStaticEntry.Builder(title)
+ .setSummary("OK")
+ .setPendingIntent(
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.workProfile().userHandle(),
+ explicit
+ )
+ )
+
+ private fun staticEntryForPrivateBuilder(
+ title: CharSequence = "Unknown",
+ explicit: Boolean = true
+ ) =
+ SafetyCenterStaticEntry.Builder(title)
+ .setSummary("OK")
+ .setPendingIntent(
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.privateProfile().userHandle(),
+ explicit
+ )
+ )
+
+ private fun createStaticEntryForWork(explicit: Boolean = true): SafetyCenterStaticEntry =
+ staticEntryForWorkBuilder(explicit = explicit).build()
+
+ private fun createStaticEntryForPrivate(explicit: Boolean = true): SafetyCenterStaticEntry =
+ staticEntryForPrivateBuilder(explicit = explicit).build()
+
+ private fun createStaticEntryForWorkPaused(): SafetyCenterStaticEntry =
+ staticEntryForWorkBuilder(explicit = false)
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
.build()
+ private val staticEntryForWorkPausedUpdated: SafetyCenterStaticEntry
+ get() =
+ staticEntryForWorkBuilder(title = "Unspecified title for Work")
+ .setSummary(safetyCenterResourcesApk.getStringByName("work_profile_paused"))
+ .build()
+
+ private val staticEntryForWorkUpdated: SafetyCenterStaticEntry
+ get() =
+ SafetyCenterStaticEntry.Builder("Unspecified title for Work")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
+ .build()
+
+ private val staticEntryForPrivateUpdated: SafetyCenterStaticEntry
+ get() =
+ SafetyCenterStaticEntry.Builder("Unspecified title for Private")
+ .setSummary("Unspecified summary")
+ .setPendingIntent(safetySourceTestData.createTestActivityRedirectPendingIntent())
+ .build()
+
private val safetyCenterDataForAdditionalUser
get() =
SafetyCenterData(
@@ -298,34 +388,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 +415,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 +423,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 +463,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 +490,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 +502,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 +523,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 +544,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,8 +565,8 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasNoWorkProfile
+ @EnsureHasNoPrivateProfile
fun getSafetyCenterData_withComplexConfigWithoutWorkProfile_returnsPrimaryDataFromConfig() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
@@ -506,9 +581,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,14 +595,19 @@ 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)
+ @EnsureHasNoPrivateProfile
fun getSafetyCenterData_withComplexConfigWithoutDataProvided_returnsDataFromConfig() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
@@ -544,9 +622,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 +647,12 @@ class SafetyCenterMultiUsersTest {
listOf(
SafetyCenterStaticEntryGroup(
"OK",
- listOf(staticEntry, staticEntryForWork, staticEntry, staticEntryForWork)
+ listOf(
+ createStaticEntry(),
+ createStaticEntryForWork(),
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
+ )
)
)
)
@@ -579,8 +660,8 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ @EnsureHasNoPrivateProfile
fun getSafetyCenterData_withComplexConfigWithPrimaryDataProvided_returnsPrimaryDataProvided() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
updatePrimaryProfileSources()
@@ -622,9 +703,9 @@ class SafetyCenterMultiUsersTest {
"OK",
listOf(
staticEntryUpdated,
- staticEntryForWork,
- staticEntry,
- staticEntryForWork
+ createStaticEntryForWork(),
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
)
)
)
@@ -633,9 +714,9 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Postsubmit(reason = "Test takes too much time to setup")
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
- fun getSafetyCenterData_withComplexConfigWithAllDataProvided_returnsAllDataProvided() {
+ @EnsureHasNoPrivateProfile
+ fun getSafetyCenterData_withComplexConfigWithExtraWorkOnlyWithAllDataProvided_returnsAllDataProvided() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
updatePrimaryProfileSources()
updateWorkProfileSources()
@@ -733,8 +814,8 @@ class SafetyCenterMultiUsersTest {
listOf(
staticEntryUpdated,
staticEntryForWorkUpdated,
- staticEntry,
- staticEntryForWork
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
)
)
)
@@ -743,16 +824,275 @@ class SafetyCenterMultiUsersTest {
}
@Test
+ @RequiresFlagsDisabled(com.android.permission.flags.Flags.FLAG_PRIVATE_PROFILE_SUPPORTED)
@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() {
+ @EnsureHasPrivateProfile(installInstrumentedApp = TRUE)
+ fun getSafetyCenterData_withComplexConfigWithPrivateProfileDisallowedWithAllDataProvided_returnsAllDataProvided() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
updatePrimaryProfileSources()
updateWorkProfileSources()
+ updatePrivateProfileSources()
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val managedUserId = deviceState.workProfile().id()
+ val safetyCenterDataFromComplexConfig =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusCritical(11),
+ 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
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_DISABLED_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_HIDDEN_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ managedUserId,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ )
+ ),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .setSummary("Critical summary")
+ .setEntries(
+ listOf(
+ dynamicBareboneUpdated,
+ dynamicDisabledUpdated,
+ dynamicDisabledForWorkUpdated,
+ dynamicHiddenUpdated,
+ dynamicHiddenForWorkUpdated
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
+ ),
+ SafetyCenterEntryOrGroup(
+ staticGroupBuilder
+ .setEntries(
+ listOf(staticBarebone, staticAllOptional, staticAllOptionalForWork)
+ )
+ .build()
+ )
+ ),
+ listOf(
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ staticEntryUpdated,
+ staticEntryForWorkUpdated,
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false)
+ )
+ )
+ )
+ )
+ assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
+ }
+
+ // TODO(b/286539356) add the os feature flag requirement when available.
+ @Test
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_PRIVATE_PROFILE_SUPPORTED)
+ @EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ @EnsureHasPrivateProfile(installInstrumentedApp = TRUE)
+ fun getSafetyCenterData_withComplexConfigWithAllDataProvided_returnsAllDataProvided() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
+ updatePrimaryProfileSources()
+ updateWorkProfileSources()
+ updatePrivateProfileSources()
+
+ val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
+
+ val managedUserId = deviceState.workProfile().id()
+ val privateProfileId = deviceState.privateProfile().id()
+ val safetyCenterDataFromComplexConfig =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusCritical(11),
+ 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
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_DISABLED_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_HIDDEN_ID,
+ managedUserId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ managedUserId,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ managedUserId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_DISABLED_ID,
+ privateProfileId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_HIDDEN_ID,
+ privateProfileId,
+ groupId = DYNAMIC_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ privateProfileId,
+ attributionTitle = null,
+ groupId = ISSUE_ONLY_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ DYNAMIC_IN_STATELESS_ID,
+ privateProfileId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ ),
+ safetyCenterTestData.safetyCenterIssueInformation(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ privateProfileId,
+ groupId = MIXED_STATELESS_GROUP_ID
+ )
+ ),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(DYNAMIC_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING)
+ .setSummary("Critical summary")
+ .setEntries(
+ listOf(
+ dynamicBareboneUpdated,
+ dynamicDisabledUpdated,
+ dynamicDisabledForWorkUpdated,
+ dynamicDisabledForPrivateUpdated,
+ dynamicHiddenUpdated,
+ dynamicHiddenForWorkUpdated,
+ dynamicHiddenForPrivateUpdated
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
+ ),
+ SafetyCenterEntryOrGroup(
+ staticGroupBuilder
+ .setEntries(
+ listOf(
+ staticBarebone,
+ staticAllOptional,
+ staticAllOptionalForWork,
+ staticAllOptionalForPrivate
+ )
+ )
+ .build()
+ )
+ ),
+ listOf(
+ SafetyCenterStaticEntryGroup(
+ "OK",
+ listOf(
+ staticEntryUpdated,
+ staticEntryForWorkUpdated,
+ staticEntryForPrivateUpdated,
+ createStaticEntry(explicit = false),
+ createStaticEntryForWork(explicit = false),
+ createStaticEntryForPrivate(explicit = false)
+ )
+ )
+ )
+ )
+ assertThat(apiSafetyCenterData.withoutExtras()).isEqualTo(safetyCenterDataFromComplexConfig)
+ }
+ @Test
+ @EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ @EnsureHasNoPrivateProfile
+ fun getSafetyCenterData_withQuietMode_shouldHaveWorkProfilePausedSummaryAndNoWorkIssues() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexAllProfileConfig)
+ updatePrimaryProfileSources()
+ updateWorkProfileSources()
setQuietMode(true)
+
val apiSafetyCenterData = safetyCenterManager.getSafetyCenterDataWithPermission()
val safetyCenterDataFromComplexConfig =
@@ -768,8 +1108,9 @@ class SafetyCenterMultiUsersTest {
listOf(
dynamicBareboneUpdated,
dynamicDisabledUpdated,
- dynamicDisabledForWorkPaused,
- dynamicHiddenUpdated
+ dynamicDisabledForWorkPausedUpdated,
+ dynamicHiddenUpdated,
+ dynamicHiddenForWorkPausedUpdated,
)
)
.setSeverityUnspecifiedIconType(
@@ -794,9 +1135,9 @@ class SafetyCenterMultiUsersTest {
"OK",
listOf(
staticEntryUpdated,
- staticEntryForWorkPaused,
- staticEntry,
- staticEntryForWorkPaused
+ staticEntryForWorkPausedUpdated,
+ createStaticEntry(explicit = false),
+ createStaticEntryForWorkPaused()
)
)
)
@@ -807,7 +1148,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,8 +1170,8 @@ class SafetyCenterMultiUsersTest {
}
@Test
- @Ignore // Removing a managed profile causes a refresh, which makes some tests flaky.
@EnsureHasWorkProfile(installInstrumentedApp = TRUE)
+ @EnsureHasNoPrivateProfile
fun getSafetyCenterData_afterManagedProfileRemoved_returnsDefaultData() {
safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
val managedSafetyCenterManager =
@@ -845,9 +1185,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(
@@ -904,8 +1242,78 @@ class SafetyCenterMultiUsersTest {
}
@Test
+ @RequiresFlagsEnabled(com.android.permission.flags.Flags.FLAG_PRIVATE_PROFILE_SUPPORTED)
+ @EnsureHasPrivateProfile(installInstrumentedApp = TRUE)
+ fun getSafetyCenterData_afterPrivateProfileRemoved_returnsDefaultData() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.singleSourceAllProfileConfig)
+ val privateSafetyCenterManager =
+ getSafetyCenterManagerForUser(deviceState.privateProfile().userHandle())
+ val safetyCenterDataWithPrivateProfile =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ SafetyCenterEntryGroup.Builder(SINGLE_SOURCE_GROUP_ID, "OK")
+ .setSeverityLevel(ENTRY_SEVERITY_LEVEL_UNKNOWN)
+ .setSummary(
+ safetyCenterResourcesApk.getStringByName("group_unknown_summary")
+ )
+ .setEntries(
+ listOf(
+ safetyCenterTestData.safetyCenterEntryDefault(
+ SINGLE_SOURCE_ALL_PROFILE_ID
+ ),
+ safetyCenterTestData.safetyCenterEntryDefault(
+ SINGLE_SOURCE_ALL_PROFILE_ID,
+ deviceState.privateProfile().id(),
+ title = "Unknown",
+ pendingIntent =
+ createTestActivityRedirectPendingIntentForUser(
+ deviceState.privateProfile().userHandle()
+ )
+ )
+ )
+ )
+ .setSeverityUnspecifiedIconType(
+ SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION
+ )
+ .build()
+ )
+ ),
+ emptyList()
+ )
+
+ assertThat(safetyCenterManager.getSafetyCenterDataWithPermission())
+ .isEqualTo(safetyCenterDataWithPrivateProfile)
+ assertThat(
+ privateSafetyCenterManager.getSafetyCenterDataWithInteractAcrossUsersPermission()
+ )
+ .isEqualTo(safetyCenterDataWithPrivateProfile)
+
+ deviceState.privateProfile().remove()
+
+ val safetyCenterDataForPrimaryUser =
+ SafetyCenterData(
+ safetyCenterTestData.safetyCenterStatusUnknown,
+ emptyList(),
+ listOf(
+ SafetyCenterEntryOrGroup(
+ safetyCenterTestData.safetyCenterEntryDefault(SINGLE_SOURCE_ALL_PROFILE_ID)
+ )
+ ),
+ emptyList()
+ )
+ assertThat(safetyCenterManager.getSafetyCenterDataWithPermission())
+ .isEqualTo(safetyCenterDataForPrimaryUser)
+ assertThat(
+ privateSafetyCenterManager.getSafetyCenterDataWithInteractAcrossUsersPermission()
+ )
+ .isEqualTo(SafetyCenterTestData.DEFAULT)
+ }
+
+ @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 +1334,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 +1351,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 +1368,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 +1389,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 +1410,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 +1430,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 +1451,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 +1478,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 +1494,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 +1519,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 +1566,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 +1591,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 +1610,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 +1625,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 +1659,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) =
@@ -1255,6 +1683,11 @@ class SafetyCenterMultiUsersTest {
.safetyCenterEntryOkBuilder(sourceId, managedUserId, title = "Ok title for Work")
.build()
+ private fun safetyCenterEntryOkForPrivate(sourceId: String, managedUserId: Int) =
+ safetyCenterTestData
+ .safetyCenterEntryOkBuilder(sourceId, managedUserId, title = "Ok title for Private")
+ .build()
+
private fun updatePrimaryProfileSources() {
safetyCenterTestHelper.setData(
DYNAMIC_BAREBONE_ID,
@@ -1307,4 +1740,29 @@ class SafetyCenterMultiUsersTest {
SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
)
}
+
+ private fun updatePrivateProfileSources() {
+ val privateSafetyCenterManager =
+ getSafetyCenterManagerForUser(deviceState.privateProfile().userHandle())
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ DYNAMIC_DISABLED_ID,
+ safetySourceTestData.informationWithIssueForPrivate
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ DYNAMIC_HIDDEN_ID,
+ safetySourceTestData.informationWithIssueForPrivate
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ ISSUE_ONLY_ALL_OPTIONAL_ID,
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ DYNAMIC_IN_STATELESS_ID,
+ safetySourceTestData.unspecifiedWithIssueForPrivate
+ )
+ privateSafetyCenterManager.setSafetySourceDataWithInteractAcrossUsersPermission(
+ ISSUE_ONLY_IN_STATELESS_ID,
+ SafetySourceTestData.issuesOnly(safetySourceTestData.informationIssue)
+ )
+ }
}
diff --git a/tests/functional/safetycenter/safetycenteractivity/Android.bp b/tests/functional/safetycenter/safetycenteractivity/Android.bp
index af0020e91..ea5f9f286 100644
--- a/tests/functional/safetycenter/safetycenteractivity/Android.bp
+++ b/tests/functional/safetycenter/safetycenteractivity/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
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/TEST_MAPPING b/tests/functional/safetycenter/safetycenteractivity/TEST_MAPPING
index 533b4d2a5..a7ffdf96d 100644
--- a/tests/functional/safetycenter/safetycenteractivity/TEST_MAPPING
+++ b/tests/functional/safetycenter/safetycenteractivity/TEST_MAPPING
@@ -9,7 +9,17 @@
"name": "SafetyCenterActivityFunctionalTestCases[com.google.android.permission.apex]",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "SafetyCenterActivityFunctionalTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
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/Android.bp b/tests/functional/safetycenter/singleuser/Android.bp
index 995f3ca40..fa7b6475b 100644
--- a/tests/functional/safetycenter/singleuser/Android.bp
+++ b/tests/functional/safetycenter/singleuser/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
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/TEST_MAPPING b/tests/functional/safetycenter/singleuser/TEST_MAPPING
index 8285ecd5a..3eec3e812 100644
--- a/tests/functional/safetycenter/singleuser/TEST_MAPPING
+++ b/tests/functional/safetycenter/singleuser/TEST_MAPPING
@@ -9,7 +9,17 @@
"name": "SafetyCenterFunctionalTestCases[com.google.android.permission.apex]",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "SafetyCenterFunctionalTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
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..4f06c0f3f 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,9 @@
package android.safetycenter.functional
-import android.app.PendingIntent
+import android.Manifest.permission.MANAGE_SAFETY_CENTER
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 +62,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 +115,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 +130,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,588 +145,657 @@ 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()
+ @get:Rule(order = 1) val supportsSafetyCenterRule = SupportsSafetyCenterRule(context)
+ @get:Rule(order = 2) val safetyCenterTestRule = SafetyCenterTestRule(safetyCenterTestHelper)
- @Before
- fun assumeDeviceSupportsSafetyCenterToRunTests() {
- assumeTrue(shouldRunTests)
- }
+ @Test
+ fun getSafetySourceData_differentPackageWithManageSafetyCenterPermission_returnsData() {
+ safetyCenterTestHelper.setConfig(safetyCenterTestConfigs.complexConfig)
- @Before
- fun enableSafetyCenterBeforeTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.setup()
- }
+ val data =
+ callWithShellPermissionIdentity(MANAGE_SAFETY_CENTER) {
+ safetyCenterManager.getSafetySourceData(DYNAMIC_OTHER_PACKAGE_ID)
+ }
- @After
- fun clearDataAfterTest() {
- if (!shouldRunTests) {
- return
- }
- safetyCenterTestHelper.reset()
+ assertThat(data).isNull()
}
@Test
@@ -808,9 +876,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 +900,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 +926,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 +1007,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 +1150,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 +1166,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 +1179,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 +1207,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 +1281,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 +1361,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 +1386,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 +1411,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 +1436,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 +1461,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 +1486,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 +1511,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 +1531,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 +1567,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 +1587,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 +1623,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 +1649,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 +1687,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 +1767,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 +1799,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 +1829,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 +1859,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 +1887,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 +1924,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 +1961,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 +2000,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 +2032,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 +2108,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 +2149,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 +2187,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 +2225,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 +2263,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 +2317,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 +2380,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 +2443,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 +2504,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 +3038,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 +3052,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 +3547,7 @@ class SafetyCenterManagerTest {
assertThat(error)
.isEqualTo(
SafetyCenterErrorDetails(
- safetyCenterResourcesContext.getStringByName("resolving_action_error")
+ safetyCenterResourcesApk.getStringByName("resolving_action_error")
)
)
}
@@ -3374,7 +3581,7 @@ class SafetyCenterManagerTest {
assertThat(error)
.isEqualTo(
SafetyCenterErrorDetails(
- safetyCenterResourcesContext.getStringByName("resolving_action_error")
+ safetyCenterResourcesApk.getStringByName("resolving_action_error")
)
)
}
@@ -3538,46 +3745,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 +3796,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
@@ -3658,9 +3836,9 @@ class SafetyCenterManagerTest {
companion object {
private val RESURFACE_DELAY = Duration.ofMillis(500)
- // Wait 1.5 times the RESURFACE_DELAY before asserting whether an issue has or has not
+ // Wait 3 times the RESURFACE_DELAY before asserting whether an issue has or has not
// resurfaced. Use a constant additive error buffer if we increase the delay considerably.
- private val RESURFACE_TIMEOUT = RESURFACE_DELAY.multipliedBy(3).dividedBy(2)
+ private val RESURFACE_TIMEOUT = RESURFACE_DELAY.multipliedBy(3)
// Check more than once during a RESURFACE_DELAY before asserting whether an issue has or
// has not resurfaced. Use a different check logic (focused at the expected resurface time)
// if we increase the delay considerably.
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..097a99612
--- /dev/null
+++ b/tests/functional/safetycenter/subpages/TEST_MAPPING
@@ -0,0 +1,27 @@
+{
+ "presubmit": [
+ {
+ "name": "SafetyCenterSubpagesTestCases"
+ }
+ ],
+ "mainline-presubmit": [
+ {
+ "name": "SafetyCenterSubpagesTestCases[com.google.android.permission.apex]",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "SafetyCenterSubpagesTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.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..10258f95b 100644
--- a/tests/hostside/safetycenter/Android.bp
+++ b/tests/hostside/safetycenter/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -27,6 +28,7 @@ java_test_host {
libs: [
"tradefed",
"junit",
+ "compatibility-host-util",
],
static_libs: [
"cts-statsd-atom-host-test-utils",
@@ -36,4 +38,4 @@ java_test_host {
"general-tests",
"mts-permission",
],
-} \ No newline at end of file
+}
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/TEST_MAPPING b/tests/hostside/safetycenter/TEST_MAPPING
index ba303cf90..798c5e485 100644
--- a/tests/hostside/safetycenter/TEST_MAPPING
+++ b/tests/hostside/safetycenter/TEST_MAPPING
@@ -8,5 +8,10 @@
{
"name": "SafetyCenterHostSideTestCases[com.google.android.permission.apex]"
}
+ ],
+ "permission-mainline-presubmit": [
+ {
+ "name": "SafetyCenterHostSideTestCases"
+ }
]
-} \ No newline at end of file
+}
diff --git a/tests/hostside/safetycenter/helper-app/Android.bp b/tests/hostside/safetycenter/helper-app/Android.bp
index cf4372d99..04e660134 100644
--- a/tests/hostside/safetycenter/helper-app/Android.bp
+++ b/tests/hostside/safetycenter/helper-app/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_safety_center",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -30,6 +31,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/Android.bp b/tests/utils/safetycenter/Android.bp
index 1c76dc775..fab8c8dde 100644
--- a/tests/utils/safetycenter/Android.bp
+++ b/tests/utils/safetycenter/Android.bp
@@ -14,6 +14,7 @@
// limitations under the License.
//
package {
+ default_team: "trendy_team_android_permissions",
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -35,6 +36,8 @@ android_library {
"kotlinx-coroutines-android",
"safety-center-internal-data",
"safety-center-resources-lib",
+ // TODO(b/326414126): aconfig: support multi-container library
+ "com.android.permission.flags-aconfig-java",
],
apex_available: [
"com.android.permission",
diff --git a/tests/utils/safetycenter/AndroidManifest.xml b/tests/utils/safetycenter/AndroidManifest.xml
index 9d7c52e62..f0a4fcbb6 100644
--- a/tests/utils/safetycenter/AndroidManifest.xml
+++ b/tests/utils/safetycenter/AndroidManifest.xml
@@ -39,12 +39,27 @@
android:exported="false"/>
<activity android:name=".TestActivity"
+ android:theme="@style/OptOutEdgeToEdgeEnforcement"
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 +69,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..0e31b2934 100644
--- a/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
+++ b/tests/utils/safetycenter/java/com/android/safetycenter/testing/SafetyCenterTestConfigs.kt
@@ -31,6 +31,7 @@ import android.safetycenter.config.SafetySource.SAFETY_SOURCE_TYPE_STATIC
import android.safetycenter.config.SafetySourcesGroup
import androidx.annotation.RequiresApi
import com.android.modules.utils.build.SdkLevel
+import com.android.permission.flags.Flags
import com.android.safetycenter.testing.SettingsPackage.getSettingsPackageName
import java.security.MessageDigest
@@ -49,7 +50,7 @@ class SafetyCenterTestConfigs(private val context: Context) {
context.packageName,
PackageInfoFlags.of(GET_SIGNING_CERTIFICATES.toLong())
)
- .signingInfo
+ .signingInfo!!
.apkContentsSigners[0]
.toByteArray()
)
@@ -64,7 +65,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 +160,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 +359,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 +386,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 +394,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 +412,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 =
@@ -640,6 +687,11 @@ class SafetyCenterTestConfigs(private val context: Context) {
.setSummaryResId(Resources.ID_NULL)
.setIntentAction(null)
.setInitialDisplayState(SafetySource.INITIAL_DISPLAY_STATE_HIDDEN)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(Resources.ID_NULL)
+ }
+ }
.build()
)
.build()
@@ -742,6 +794,11 @@ class SafetyCenterTestConfigs(private val context: Context) {
dynamicSafetySourceBuilder(id)
.setProfile(SafetySource.PROFILE_ALL)
.setTitleForWorkResId(android.R.string.paste)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(android.R.string.unknownName)
+ }
+ }
private fun staticSafetySource(id: String) = staticSafetySourceBuilder(id).build()
@@ -750,13 +807,18 @@ 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) =
staticSafetySourceBuilder(id)
.setProfile(SafetySource.PROFILE_ALL)
.setTitleForWorkResId(android.R.string.paste)
+ .apply {
+ if (SdkLevel.isAtLeastV() && Flags.privateProfileTitleApi()) {
+ setTitleForPrivateProfileResId(android.R.string.unknownName)
+ }
+ }
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private fun issueOnlySafetySourceWithDuplicationInfo(id: String, deduplicationGroup: String) =
@@ -780,6 +842,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 +872,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 +1100,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 a6fbb89b9..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,7 +118,7 @@ class SafetyCenterTestData(context: Context) {
numTipIssues: Int,
): SafetyCenterStatus =
SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
getIcuPluralsString("overall_severity_level_tip_summary", numTipIssues)
)
.setSeverityLevel(OVERALL_SEVERITY_LEVEL_OK)
@@ -131,7 +132,7 @@ class SafetyCenterTestData(context: Context) {
numAutomaticIssues: Int,
): SafetyCenterStatus =
SafetyCenterStatus.Builder(
- safetyCenterResourcesContext.getStringByName("overall_severity_level_ok_title"),
+ safetyCenterResourcesApk.getStringByName("overall_severity_level_ok_title"),
getIcuPluralsString(
"overall_severity_level_action_taken_summary",
numAutomaticIssues
@@ -146,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)
@@ -163,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)
@@ -180,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()
/**
@@ -196,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)
/**
@@ -213,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)
@@ -236,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)
/**
@@ -261,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()
@@ -273,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()
@@ -304,7 +310,7 @@ class SafetyCenterTestData(context: Context) {
userId
),
"Review",
- safetySourceTestData.testActivityRedirectPendingIntent
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -344,7 +350,7 @@ class SafetyCenterTestData(context: Context) {
userId
),
"See issue",
- safetySourceTestData.testActivityRedirectPendingIntent
+ safetySourceTestData.createTestActivityRedirectPendingIntent()
)
.apply {
if (confirmationDialog && SdkLevel.isAtLeastU()) {
@@ -425,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..7e77c0827 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,25 @@ class SafetySourceTestData(private val context: Context) {
"Unspecified summary",
SEVERITY_LEVEL_UNSPECIFIED
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .build()
+ )
+ .addIssue(informationIssue)
+ .build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
+ * a [SEVERITY_LEVEL_UNSPECIFIED] [SafetySourceStatus], to be used for a private profile entry.
+ */
+ val unspecifiedWithIssueForPrivate =
+ SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "Unspecified title for Private",
+ "Unspecified summary",
+ SEVERITY_LEVEL_UNSPECIFIED
+ )
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssue)
@@ -183,7 +198,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 +224,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 +240,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 +256,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 +272,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 +294,25 @@ class SafetySourceTestData(private val context: Context) {
"Ok summary",
SEVERITY_LEVEL_INFORMATION
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
+ .build()
+ )
+ .addIssue(informationIssue)
+ .build()
+
+ /**
+ * A [SafetySourceData] with a [SEVERITY_LEVEL_INFORMATION] redirecting [SafetySourceIssue] and
+ * [SafetySourceStatus], to be used for a private profile entry.
+ */
+ val informationWithIssueForPrivate =
+ SafetySourceData.Builder()
+ .setStatus(
+ SafetySourceStatus.Builder(
+ "Ok title for Private",
+ "Ok summary",
+ SEVERITY_LEVEL_INFORMATION
+ )
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(informationIssue)
@@ -289,7 +326,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 +352,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
RECOMMENDATION_ISSUE_ACTION_ID,
"See issue",
- testActivityRedirectPendingIntent
+ createTestActivityRedirectPendingIntent()
)
.apply {
if (confirmationDialog && SdkLevel.isAtLeastU()) {
@@ -383,7 +420,7 @@ class SafetySourceTestData(private val context: Context) {
"Recommendation summary",
SEVERITY_LEVEL_RECOMMENDATION
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
@@ -425,12 +462,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 +493,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 +518,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 +532,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 +555,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 +583,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
CRITICAL_ISSUE_ACTION_ID,
"Go solve issue",
- testActivityRedirectPendingIntent
+ createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -520,7 +611,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 +622,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 +631,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 +663,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 +681,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical summary",
SEVERITY_LEVEL_CRITICAL_WARNING
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
@@ -635,6 +745,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 +784,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 +813,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 +856,7 @@ class SafetySourceTestData(private val context: Context) {
"Critical summary 2",
SEVERITY_LEVEL_CRITICAL_WARNING
)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.addIssue(criticalRedirectingIssue)
@@ -729,7 +875,7 @@ class SafetySourceTestData(private val context: Context) {
SafetySourceData.Builder()
.setStatus(
SafetySourceStatus.Builder(entryTitle, entrySummary, severityLevel)
- .setPendingIntent(testActivityRedirectPendingIntent)
+ .setPendingIntent(createTestActivityRedirectPendingIntent())
.build()
)
.apply {
@@ -746,7 +892,7 @@ class SafetySourceTestData(private val context: Context) {
Action.Builder(
"action_id",
"Action",
- testActivityRedirectPendingIntent
+ createTestActivityRedirectPendingIntent()
)
.build()
)
@@ -784,7 +930,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 +959,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() }
+ }
+}
diff --git a/tests/utils/safetycenter/res/values/styles.xml b/tests/utils/safetycenter/res/values/styles.xml
new file mode 100644
index 000000000..ce54568ed
--- /dev/null
+++ b/tests/utils/safetycenter/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!--
+ TODO(b/309578419): Make activities handle insets properly and then remove this.
+ -->
+ <style name="OptOutEdgeToEdgeEnforcement">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+</resources>